first commit

This commit is contained in:
Zeeshaun
2026-03-11 11:18:25 -05:00
commit f3cb730f3b
472 changed files with 53651 additions and 0 deletions
@@ -0,0 +1,14 @@
using System.Xml.Linq;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class AppDetailBankcardSection : AppDetailsSection
{
public AppDetailBankcardSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "appDetailBankcard", settings)
{
}
}
}
@@ -0,0 +1,89 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Assets;
using Landmark.OLA.Models.Loan;
namespace CRIF.ACTion.Loan
{
public class AppDetailHelocSection : AppDetailsSection
{
private readonly List<XObject> _content;
private readonly XElement _fieldsElement;
public AppDetailHelocSection(XNamespace sectionNamespace, CrifSettings settings,
string interestRateType = null, string calculationType = null)
: base(sectionNamespace, "appDetailHELOC", settings, interestRateType, calculationType)
{
_content = [];
}
private protected AppDetailHelocSection(XNamespace sectionNamespace, CrifSettings settings,
string elementName, string interestRateType = null, string calculationType = null)
: base(sectionNamespace, elementName, settings, interestRateType, calculationType)
{
_content = [];
}
public virtual void AddPropertyDetails(RealEstateBase realEstateBase, bool? mfgHomeIncludesLand = null)
{
var appDetailPropertiesElement = new XElement(CrifNamespaces.App + "appDetailProperties");
var propertyType = "RealEstate";
var collateralType = CrifConstants.CollateralIds[CrifConstants.Collateral.ResidentialRealEstate];
string subType;
if (mfgHomeIncludesLand.HasValue)
{
propertyType = "MfgHomeAndLand";
collateralType = mfgHomeIncludesLand.Value ?
CrifConstants.CollateralIds[CrifConstants.Collateral.ManufacturedHomeAndLand] :
CrifConstants.CollateralIds[CrifConstants.Collateral.ManufacturedHome];
subType = ((ManufacturedHome)realEstateBase).Type.Value.ToString();
}
else
{
subType = ((RealEstate)realEstateBase).Type.Value.ToString();
}
var statusID = "Active";
var includeInLTVCalculations = true;
var appDetailPropertyElement = new XElement(CrifNamespaces.App + "appDetailProperty",
new XAttribute(CrifNamespaces.Common + "idRef", realEstateBase.GetOrCreateGuid()),
new XAttribute(CrifNamespaces.Common + "propertyType", propertyType),
new XAttribute(CrifNamespaces.Common + "propertySubType", subType),
new XAttribute(CrifNamespaces.App + "isCollateralFL", realEstateBase.IsCollateral),
new XAttribute(CrifNamespaces.App + "isTradeInFL", realEstateBase.IsTradeIn),
new XAttribute(CrifNamespaces.App + "statusID", statusID),
new XAttribute(CrifNamespaces.App + "includeInLTVCalculations", includeInLTVCalculations),
new XAttribute(CrifNamespaces.App + "collateralTypeID", collateralType)
);
appDetailPropertiesElement.Add(appDetailPropertyElement);
_content.Add(appDetailPropertiesElement);
}
public void AddHmdaDisclosed(bool? acceptedDisclosure)
{
if ((!acceptedDisclosure.HasValue) || !acceptedDisclosure.Value)
{
return;
}
var hmdaElement = new XElement(CrifNamespaces.App + "appDetailHMDA2018",
new XAttribute(CrifNamespaces.App + "isNoticeDisclosedFL", acceptedDisclosure.Value));
_content.Add(hmdaElement);
}
public override void SetDetails(LoanApplication loanApplication, Guid channelRef, object[] additionalContent = null, XElement fieldsElement = null, XElement vehicleValuationsElement = null)
{
base.SetDetails(loanApplication, channelRef, [.. _content], _fieldsElement);
}
}
}
@@ -0,0 +1,13 @@
using System.Xml.Linq;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class AppDetailSignatureSection : AppDetailsSection
{
public AppDetailSignatureSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "appDetail", settings, "Fixed", "Installment")
{
}
}
}
@@ -0,0 +1,237 @@
using System.Xml.Linq;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using CRIF.ACTion.Services.Landmark;
using Landmark.OLA.Models.Loan;
using Landmark.OLA.Models.Loan.Enums;
namespace CRIF.ACTion.Loan
{
public class AppDetailsSection : CrifRequestSection
{
private readonly string _elementName;
private readonly string _interestRateType;
private readonly string _calculationType;
private readonly ProductIdService _productIdTranslator;
protected AppDetailsSection(XNamespace sectionNamespace, string elementName, CrifSettings settings,
string interestRateType = null, string calculationType = null)
: base(sectionNamespace, "appDetails", settings)
{
_elementName = elementName;
_interestRateType = string.IsNullOrWhiteSpace(interestRateType) ? "Variable" : interestRateType;
_calculationType = string.IsNullOrWhiteSpace(calculationType) ? "OpenEnd" : calculationType;
_productIdTranslator = new ProductIdService(settings);
}
public static bool IsHMDA(LoanType? loanType)
{
HMDALoanType hmdaType;
try
{
hmdaType = (HMDALoanType)Enum.Parse(typeof(HMDALoanType), loanType.ToString());
}
catch
{
return false;
}
return true;
}
public virtual void SetDetails(LoanApplication loanApplication, Guid channelRef, object[] additionalContent = null, XElement fieldsElement = null, XElement vehicleValuationsElement = null)
{
var appDetailElement = new XElement(CrifNamespaces.App + _elementName,
new XAttribute(CrifNamespaces.App + "contractRate", 0m), //Per Ben V. at Landmark, we should send in 0 so that CRIF can set the correct rate
new XAttribute(CrifNamespaces.App + "financedAmt", loanApplication.RequestedAmount),
new XAttribute(CrifNamespaces.App + "termFrequencyCD", "Monthly"),
new XAttribute(CrifNamespaces.App + "calculationType", _calculationType),
new XAttribute(CrifNamespaces.App + "loanType", "Consumer"),
new XAttribute(CrifNamespaces.App + "interestRateType", _interestRateType),
new XAttribute(CrifNamespaces.App + "rankNum", 1),
new XAttribute(CrifNamespaces.App + "isApplicantRequestedInterestRate", false),
new XAttribute(CrifNamespaces.App + "isApplicantRequestedAmountRequested", true),
new XAttribute(CrifNamespaces.App + "principal", loanApplication.RequestedAmount), //TODO #234: Why is the loan amount listed twice?
new XAttribute(CrifNamespaces.App + "isApplicantRequestedTerm", true),
new XAttribute(CrifNamespaces.App + "isHMDAFL", IsHMDA(loanApplication.LoanType))
);
if (loanApplication.TermLengthInMonths.HasValue)
{
appDetailElement.Add(new XAttribute(CrifNamespaces.App + "termInMonths", loanApplication.TermLengthInMonths.Value));
}
var appDetailParties = new XElement(CrifNamespaces.App + "appDetailParties",
new XElement(CrifNamespaces.App + "appDetailParty",
new XAttribute(CrifNamespaces.Common + "idRef", channelRef),
new XAttribute(CrifNamespaces.Common + "partyType", "Channel"),
new XAttribute(CrifNamespaces.App + "appPartyType", "ServicingChannel")));
var appDetailRequestedAmts = new XElement(CrifNamespaces.App + "appDetailRequestedAmts",
new XElement(CrifNamespaces.App + "appDetailRequestedAmt",
new XAttribute(CrifNamespaces.App + "requestedAmount", loanApplication.RequestedAmount), //TODO #234: Financed amount is listed 3 times? Are the other 2 from the calculator?
new XAttribute(CrifNamespaces.App + "requestedAmountType", "Generic")));
var product = new XElement(CrifNamespaces.App + "product",
new XAttribute(CrifNamespaces.Common + "productID",
_productIdTranslator.GetProductId(loanApplication.LoanType.Value)));
var loanPurposes = new XElement(CrifNamespaces.App + "loanPurposes",
new XElement(CrifNamespaces.App + "appDetailLoanPurpose",
new XAttribute(CrifNamespaces.App + "rank", 1),
new XElement(CrifNamespaces.App + "loanPurpose",
new XAttribute(CrifNamespaces.Common + "CD", (int)loanApplication.Purpose.Value)
)));
appDetailElement.Add(appDetailParties);
appDetailElement.Add(additionalContent);
appDetailElement.Add(appDetailRequestedAmts);
appDetailElement.Add(product);
string productId = _productIdTranslator.GetProductId(loanApplication.LoanType.Value).ToString();
XElement appFieldsElement = null;
if (_settings.CustomFields != null && _settings.CustomFields.ProductIdToCategoryId.TryGetValue(productId, out string productCategoryId))
{
CustomPropertyStatusData customPropertyRenovationData = _settings.CustomFields.CustomPropertyRenovationFieldData.Find(x => x.ProductCategoryId == productCategoryId);
if (customPropertyRenovationData != null)
{
bool realEstateNotNullValue = loanApplication.RealEstate?.CurrentlyBeingRemodeledOrImproved ?? false;
string propertyRenovationValueCode = realEstateNotNullValue ? customPropertyRenovationData.YesId : customPropertyRenovationData.NoId;
appFieldsElement = GetLookupFieldLinkElement(customPropertyRenovationData.CustomFieldId, propertyRenovationValueCode, "AppDetail", appFieldsElement);
}
CustomPropertyStatusData customPropertySaleData = _settings.CustomFields.CustomPropertySaleFieldData.Find(x => x.ProductCategoryId == productCategoryId);
if (customPropertySaleData != null)
{
bool realEstateNotNullValue = loanApplication.RealEstate?.CurrentlyListedForSale ?? false;
string propertySaleValueCode = realEstateNotNullValue ? customPropertySaleData.YesId : customPropertySaleData.NoId;
appFieldsElement = GetLookupFieldLinkElement(customPropertySaleData.CustomFieldId, propertySaleValueCode, "AppDetail", appFieldsElement);
}
CustomJointCreditData customJointCreditData = _settings.CustomFields.CustomJointCreditFieldData.Find(x => x.ProductCategoryId == productCategoryId);
if (customJointCreditData != null)
{
string jointCreditValueCode = loanApplication.CoApplicant != null ? customJointCreditData.JointlyId : customJointCreditData.IndividuallyId;
appFieldsElement = GetLookupFieldLinkElement(customJointCreditData.CustomFieldId, jointCreditValueCode, "ProductCategory", appFieldsElement);
}
}
if (!string.IsNullOrWhiteSpace(loanApplication.PromoCode))
{
if (_settings.CustomFieldMappings.LoanTypeToPromoId.TryGetValue(productId, out string promoFieldId))
{
appFieldsElement = GetStringFieldLinkElement(promoFieldId, loanApplication.PromoCode, "ProductCategory", appFieldsElement);
}
}
if (loanApplication.Vehicle != null)
{
if (!string.IsNullOrWhiteSpace(loanApplication.Vehicle.LicensePlateNumber))
{
if (_settings.CustomFieldMappings.CustomFieldID.TryGetValue("LicensePlateNumber", out string customFieldId))
{
appFieldsElement = GetStringFieldLinkElement(customFieldId, loanApplication.Vehicle.LicensePlateNumber, "ProductCategory", appFieldsElement);
}
}
if (!string.IsNullOrWhiteSpace(loanApplication.Vehicle.CountyTitled))
{
if (_settings.CustomFieldMappings.CustomFieldID.TryGetValue("CountyTitled", out string customFieldId))
{
appFieldsElement = GetStringFieldLinkElement(customFieldId, loanApplication.Vehicle.CountyTitled, "ProductCategory", appFieldsElement);
}
}
if (!string.IsNullOrWhiteSpace(loanApplication.Vehicle.MunicipalityWhereKept))
{
if (_settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityWhereKept", out string customFieldId))
{
appFieldsElement = GetStringFieldLinkElement(customFieldId, loanApplication.Vehicle.MunicipalityWhereKept, "ProductCategory", appFieldsElement);
}
}
var municipalityTypeCode = GetMunicipalityTypeCode(loanApplication.Vehicle.MunicipalityType);
if (!string.IsNullOrWhiteSpace(municipalityTypeCode))
{
if (_settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityType", out string customFieldId))
{
appFieldsElement = GetLookupFieldLinkElement(customFieldId, municipalityTypeCode, "ProductCategory", appFieldsElement);
}
}
}
if (appFieldsElement != null)
{
appDetailElement.Add(appFieldsElement); //Order matters for this element.
}
appDetailElement.Add(loanPurposes);
appDetailElement.Add(vehicleValuationsElement); //Order matters for this element.
AddContent(appDetailElement);
}
private static XElement GetStringFieldLinkElement(string customFieldId, string value, string fieldLevel, XElement fieldsElement)
{
fieldsElement = CreateFieldsElementIfNull(fieldsElement);
var stringFieldLinkElement = new XElement(CrifNamespaces.Common + "stringFieldLink",
new XAttribute(CrifNamespaces.Common + "value", value));
var customFieldElement = new XElement(CrifNamespaces.Common + "customField",
new XAttribute(CrifNamespaces.Common + "ID", customFieldId),
new XAttribute(CrifNamespaces.Common + "fieldLevel", fieldLevel));
fieldsElement.Add(stringFieldLinkElement);
stringFieldLinkElement.Add(customFieldElement);
return fieldsElement;
}
private static XElement GetLookupFieldLinkElement(string Id, string value, string fieldLevel, XElement fieldsElement)
{
fieldsElement = CreateFieldsElementIfNull(fieldsElement);
var lookupFieldLinkElement = new XElement(CrifNamespaces.Common + "lookupFieldLink",
new XAttribute(CrifNamespaces.Common + "value", value),
new XElement(CrifNamespaces.Common + "customLookupField",
new XAttribute(CrifNamespaces.Common + "ID", Id),
new XAttribute(CrifNamespaces.Common + "fieldLevel", fieldLevel)));
fieldsElement.Add(lookupFieldLinkElement);
return fieldsElement;
}
private static XElement CreateFieldsElementIfNull(XElement fieldsElement)
{
if (fieldsElement == null)
{
fieldsElement = new XElement(CrifNamespaces.App + "fields");
}
return fieldsElement;
}
private string GetMunicipalityTypeCode(string municipalityType)
{
if (string.IsNullOrWhiteSpace(municipalityType))
{
return null;
}
if (_settings?.CustomFieldMappings?.MunicipalityTypeCode == null)
{
return null;
}
var key = municipalityType.Trim().ToUpperInvariant();
return _settings.CustomFieldMappings.MunicipalityTypeCode.TryGetValue(key, out string code)
? code
: null;
}
}
}
@@ -0,0 +1,35 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class AppNotesSectionBuilder : CrifRequestSection
{
public AppNotesSectionBuilder(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "appNotes", settings)
{
}
public void AddAppNote(string appNoteContent)
{
var appNote = new XElement(CrifNamespaces.App + "appNote",
new XAttribute(CrifNamespaces.App + "noteEntry", BuilderUtils.FormatCrifData(appNoteContent)),
new XAttribute(CrifNamespaces.App + "noteTypeDisplayName", "Application Note"),
new XAttribute(CrifNamespaces.App + "noteTypeCategoryDisplayName", "Application Note"));
var noteType = new XElement(CrifNamespaces.App + "noteType",
new XAttribute(CrifNamespaces.Common + "CD", 1));
var noteTypeCategory = new XElement(CrifNamespaces.App + "noteTypeCategory",
new XAttribute(CrifNamespaces.Common + "lookupType", "NoteTypeCategory"),
new XAttribute(CrifNamespaces.Common + "CD", 1));
appNote.Add(noteType, noteTypeCategory);
AddContent(appNote);
}
}
}
@@ -0,0 +1,23 @@
using System.Xml.Linq;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Assets;
namespace CRIF.ACTion.Loan.Assets
{
public class AppDetailMfgHomeAndLandSection : AppDetailHelocSection
{
//AppDetailMfgHomeAndLand is generally similar to AppDetailHeloc.
public AppDetailMfgHomeAndLandSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, settings, "appDetailMfgHousingAndLand", "Fixed", "Installment")
{
}
public override void AddPropertyDetails(RealEstateBase realEstateBase, bool? mfgHomeIncludesLand = null)
{
if (!mfgHomeIncludesLand.HasValue)
throw new ArgumentException("Manufactured Home Loans must state if the land is included.");
base.AddPropertyDetails(realEstateBase, mfgHomeIncludesLand);
}
}
}
@@ -0,0 +1,16 @@
using System.Xml.Linq;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan.Assets
{
public class AppDetailRealEstateSection : AppDetailHelocSection
{
//So far, AppDetailRealEstate is used exactly the same as AppDetailHeloc.
//The only difference is the element name and term types.
public AppDetailRealEstateSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, settings, "appDetailRealEstate", "Fixed", "Installment")
{
}
}
}
@@ -0,0 +1,69 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Assets;
using Landmark.OLA.Models.Loan;
namespace CRIF.ACTion.Loan.Assets
{
public class AppDetailVehicleSection : AppDetailsSection
{
private readonly List<XObject> _content;
private XElement _vehicleValuationsElement;
public AppDetailVehicleSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "appDetailVehicle", settings, "Fixed", "Installment")
{
_content = [];
}
public void AddPropertyDetails(Vehicle vehicle)
{
//If there's no vehicle type, it's likely a preapproval and doesn't need the appDetailProperties element
if (vehicle is null || vehicle.Type is null)
{
return;
}
var appDetailPropertiesElement = new XElement(CrifNamespaces.App + "appDetailProperties");
var propertyValues = PropertyValues.GetPropertyValues(vehicle.Type.Value);
var isCollateralFL = true;
var isTradeInFL = false;
var statusID = "Active";
var includeInLTVCalculations = true;
var appDetailPropertyElement = new XElement(CrifNamespaces.App + "appDetailProperty",
new XAttribute(CrifNamespaces.Common + "idRef", vehicle.GetOrCreateGuid()),
new XAttribute(CrifNamespaces.Common + "propertyType", propertyValues.PropertyType),
new XAttribute(CrifNamespaces.Common + "propertySubType", propertyValues.PropertySubType),
new XAttribute(CrifNamespaces.App + "isCollateralFL", isCollateralFL),
new XAttribute(CrifNamespaces.App + "isTradeInFL", isTradeInFL),
new XAttribute(CrifNamespaces.App + "statusID", statusID),
new XAttribute(CrifNamespaces.App + "includeInLTVCalculations", includeInLTVCalculations),
new XAttribute(CrifNamespaces.App + "collateralTypeID", propertyValues.CollateralId)
);
appDetailPropertiesElement.Add(appDetailPropertyElement);
_vehicleValuationsElement = new XElement(CrifNamespaces.App + "vehicleValuations",
new XElement(CrifNamespaces.App + "appDetailVehicleValuation",
new XAttribute(CrifNamespaces.Common + "idRef", vehicle.ValuationId),
new XAttribute(CrifNamespaces.App + "valueType", vehicle.ValuationType.Value.ToString())));
//TODO: What does valuationType mean?
_content.Add(appDetailPropertiesElement);
}
public void AddSalesPriceDetails(LoanApplication loanApplication)
{
_content.Add(new XAttribute(CrifNamespaces.App + "initialSalesPrice", loanApplication.RequestedAmount));
}
public override void SetDetails(LoanApplication loanApplication, Guid channelRef, object[] additionalContent = null, XElement fieldsElement = null, XElement vehicleValuationsElement = null)
{
base.SetDetails(loanApplication, channelRef, [.. _content], vehicleValuationsElement: _vehicleValuationsElement);
}
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan.Assets
{
public interface IVehicleValuations
{
VehicleValuationsSection VehicleValuations { get; }
}
}
@@ -0,0 +1,36 @@
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan.Assets
{
public class MfgHousingAndLandAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public MfgHousingAndLandAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailMfgHomeAndLandSection(createApp.CrifRequestSectionNamespace, _settings);
var includesLand = true;
if (createApp.LoanApplication.ManufacturedHome?.Lot?.IsRented ?? false)
{
includesLand = false;
}
((AppDetailMfgHomeAndLandSection)createApp.AppDetails).AddPropertyDetails(createApp.LoanApplication.ManufacturedHome, includesLand);
((AppDetailMfgHomeAndLandSection)createApp.AppDetails).AddHmdaDisclosed(createApp.LoanApplication.PrimaryBorrower.HmdaValues.HmdaNoticeDisclosed);
createApp.Properties.AddProperty(new MfgHousingProperty(createApp.LoanApplication.ManufacturedHome, includesLand));
createApp.Addresses.AddAddress(createApp.LoanApplication.ManufacturedHome.Lot, ContactMechanisms.CrifContactMechanism.RealEstateAddress);
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel);
}
}
}
@@ -0,0 +1,12 @@
using Landmark.OLA.Models.Assets;
namespace CRIF.ACTion.Loan.Assets
{
public class MfgHousingProperty : RealEstateProperty
{
public MfgHousingProperty(ManufacturedHome manufacturedHome, bool includesLand)
: base(manufacturedHome, "propertyMfgHousing", "MfgHomeAndLand")
{
}
}
}
@@ -0,0 +1,32 @@
using Landmark.OLA.Models.Assets.Enums;
namespace CRIF.ACTion.Loan.Assets
{
public static class PropertyValues
{
public static (int CollateralId, string PropertyType, string PropertySubType, string elementName) GetPropertyValues(VehicleType type)
{
switch (type)
{
case VehicleType.Automobile:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.Vehicle], "Auto", "Automobile", "propertyAuto");
case VehicleType.Motorcycle:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.Motorcycle], "Motorcycle", "NoneSelected", "propertyAuto");
case VehicleType.Boat:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.Marine], "Marine", "NoneSelected", "propertyMarine");
case VehicleType.ATV:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.ATV], "ATV", "NoneSelected", "propertyAuto");
case VehicleType.PWC:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.Marine], "Marine", "NoneSelected", "propertyMarine");
case VehicleType.RecreationalVehicle:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.RecreationalVehicle], "RV", "CampingTrailer", "propertyAuto"); //"CampingTrailer" is from CRIF example
case VehicleType.Snowmobile:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.Snowmobile], "Snowmobile", "NoneSelected", "propertyAuto");
case VehicleType.TrailerMotorHome:
return (CrifConstants.CollateralIds[CrifConstants.Collateral.RecreationalVehicle], "RV", "Other", "propertyAuto"); //"Other" is from sprint requirements doc
default:
throw new NotImplementedException("This vehicle type is not implemented.");
}
}
}
}
@@ -0,0 +1,27 @@
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan.Assets
{
public class RealEstateEquityAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public RealEstateEquityAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailRealEstateSection(createApp.CrifRequestSectionNamespace, _settings);
((AppDetailRealEstateSection)createApp.AppDetails).AddPropertyDetails(createApp.LoanApplication.RealEstate);
((AppDetailRealEstateSection)createApp.AppDetails).AddHmdaDisclosed(createApp.LoanApplication.PrimaryBorrower.HmdaValues.HmdaNoticeDisclosed);
createApp.Properties.AddProperty(new RealEstateProperty(createApp.LoanApplication.RealEstate));
createApp.Addresses.AddAddress(createApp.LoanApplication.RealEstate.RealEstateAddress, ContactMechanisms.CrifContactMechanism.RealEstateAddress);
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel);
}
}
}
@@ -0,0 +1,221 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using Landmark.OLA.Models.Assets;
using Landmark.OLA.Models.Assets.Enums;
using static CRIF.ACTion.Common.ContactMechanisms;
namespace CRIF.ACTion.Loan.Assets
{
public class RealEstateProperty : IProperty
{
private protected readonly RealEstateBase _realEstateBase;
private protected readonly string _elementName;
private protected readonly string _propertyType;
public RealEstateProperty(RealEstateBase realEstateBase, string elementName = "propertyRealEstate",
string propertyType = "RealEstate")
{
_realEstateBase = realEstateBase;
_elementName = elementName;
_propertyType = propertyType;
}
public XElement Build(params XElement[] content)
{
var classType = _realEstateBase.GetType();
if (classType.Equals(typeof(ManufacturedHome)) || classType.IsSubclassOf(typeof(ManufacturedHome)))
{
var realEstateElement = GetRealEstateElement((ManufacturedHome)_realEstateBase);
realEstateElement.Add(
new XAttribute(CrifNamespaces.Common + "lienPositionID", LienPositionType.First)
);
return realEstateElement;
}
else if (classType.Equals(typeof(RealEstate)) || classType.IsSubclassOf(typeof(RealEstate)))
{
var realEstateElement = GetRealEstateElement((RealEstate)_realEstateBase);
realEstateElement.Add(
new XAttribute(CrifNamespaces.Common + "lienPositionID", GetLienPosition(_realEstateBase.LienPosition))
);
return realEstateElement;
}
else
{
throw new ArgumentException("Unexpected object given to constructor for RealEstateProperty");
}
}
private XElement GetRealEstateElement(RealEstate realEstate)
{
return GetRealEstateElement(realEstate, realEstate.Type.Value.ToString());
}
private XElement GetRealEstateElement(ManufacturedHome manufacturedHome)
{
var element = GetRealEstateElement(manufacturedHome, manufacturedHome.Type.Value.ToString(),
manufacturedHome.EstimatedLandValuation, manufacturedHome.EstimatedHomeValuation);
//The valuation fields above are suppose to go in totalValue and finalEstimatedValue as written.
//It's a little strange but that's how CRIF stores that data.
// This has to be before propertyLot, otherwise CRIF will complain!
if (manufacturedHome.Lot.AnnualPropertyTax.HasValue)
{
var propertyTaxesElement = new XElement(CrifNamespaces.Common + "propertyTaxes");
var propertyTaxElement = new XElement(
CrifNamespaces.Common + "propertyTax",
new XAttribute(CrifNamespaces.Common + "taxEntity", "IRS"),
new XAttribute(CrifNamespaces.Common + "amount", manufacturedHome.Lot.AnnualPropertyTax.Value.ToString()),
new XAttribute(CrifNamespaces.Common + "paymentFrequency", "Annually")
);
propertyTaxesElement.Add(propertyTaxElement);
element.Add(propertyTaxesElement);
}
var propertyLotElement = new XElement(CrifNamespaces.Common + "propertyLot");
if (manufacturedHome.Lot.RentalAmt.HasValue)
{
propertyLotElement.Add(new XAttribute(CrifNamespaces.Common + "rentalAmt", manufacturedHome.Lot.RentalAmt.Value));
}
element.Add(propertyLotElement);
return element;
}
private XElement GetRealEstateElement(RealEstateBase realEstateBase, string subtype, decimal? finalEstimatedValue = null, decimal? totalValue = null)
{
var propertyRealEstateElement = new XElement(CrifNamespaces.Common + _elementName,
new XAttribute(CrifNamespaces.Common + "id", realEstateBase.Id),
new XAttribute(CrifNamespaces.Common + "propertySubType", subtype),
// new XAttribute(CrifNamespaces.Common + "hasClearTitleFL", realEstate.HasClearTitle),
new XAttribute(CrifNamespaces.Common + "initialEstimatedValue", realEstateBase.InitialEstimatedValue),
new XAttribute(CrifNamespaces.Common + "propertyType", _propertyType),
new XAttribute(CrifNamespaces.Common + "ownerOccupiedTypeID", realEstateBase.Use.Value));
if(finalEstimatedValue.HasValue)
{
propertyRealEstateElement.Add(new XAttribute(CrifNamespaces.Common + "finalEstimatedValue", finalEstimatedValue));
}
if (totalValue.HasValue)
{
propertyRealEstateElement.Add(new XAttribute(CrifNamespaces.Common + "totalValue", totalValue));
}
var contactMechanismsElement = GetContactMechanisms(realEstateBase);
propertyRealEstateElement.Add(GetPropertyDetails(realEstateBase));
propertyRealEstateElement.Add(contactMechanismsElement);
return propertyRealEstateElement;
}
private static XElement GetContactMechanisms(RealEstateBase realEstateBase)
{
var contactMechanismType = CrifContactMechanism.RealEstateAddress;
var idRef = "";
var classType = realEstateBase.GetType();
if (classType.Equals(typeof(ManufacturedHome)) || classType.IsSubclassOf(typeof(ManufacturedHome)))
{
contactMechanismType = CrifContactMechanism.ParkContactAddress;
idRef = ((ManufacturedHome)realEstateBase).Lot.Id.ToString();
}
else if (classType.Equals(typeof(RealEstate)) || classType.IsSubclassOf(typeof(RealEstate)))
{
idRef = ((RealEstate)realEstateBase).RealEstateAddress.Id.ToString();
}
var contactMechanismsElement = new XElement(CrifNamespaces.Common + "contactMechanisms",
new XElement(CrifNamespaces.Common + "contactMechanism",
new XAttribute(CrifNamespaces.Common + "contactMechanismType", contactMechanismType.ToString()),
new XElement(CrifNamespaces.Common + "addressRef",
new XAttribute(CrifNamespaces.Common + "idRef", idRef)),
new XElement(CrifNamespaces.Common + "contactMechanismCategory",
new XAttribute(CrifNamespaces.Common + "CD", (int)GetContactMechanismCategory(contactMechanismType)))
));
return contactMechanismsElement;
}
private static XAttribute[] GetPropertyDetails(RealEstateBase realEstateBase)
{
var attributeList = new List<XAttribute>();
if (realEstateBase.HasSpa.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "hasSpaFL", realEstateBase.HasSpa.Value));
}
if (realEstateBase.HasPool.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "hasPoolFL", realEstateBase.HasPool.Value));
}
if (realEstateBase.HasBasement.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "hasBasementFL", realEstateBase.HasBasement.Value));
}
if (realEstateBase.HasCentralAir.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "hasCentralAirFL", realEstateBase.HasCentralAir.Value));
}
if (realEstateBase.HasGarage.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "hasGarageFL", realEstateBase.HasGarage.Value));
}
if (realEstateBase.InFloodZone.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "inFloodZoneFL", realEstateBase.InFloodZone.Value));
}
if (realEstateBase.InEquity.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "inEquityFL", realEstateBase.InEquity.Value));
}
if (realEstateBase.IsPropertyHeldInTrust.HasValue)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "isPropertyHeldInTrust", realEstateBase.IsPropertyHeldInTrust.Value));
}
return [.. attributeList];
}
public static LienPositionType GetLienPosition(LienPosition lienPositionData)
{
if (lienPositionData.HasFirstMortgage && lienPositionData.HasSecondMortgage)
{
if (lienPositionData.PayoffFirstMortgage && lienPositionData.PayoffSecondMortgage)
{
return LienPositionType.First;
}
else if (!lienPositionData.PayoffFirstMortgage && !lienPositionData.PayoffSecondMortgage)
{
return LienPositionType.Third;
}
else if (lienPositionData.PayoffFirstMortgage || lienPositionData.PayoffSecondMortgage)
{
return LienPositionType.Second;
}
}
else if (lienPositionData.HasFirstMortgage || lienPositionData.HasSecondMortgage)
{
if (lienPositionData.PayoffFirstMortgage || lienPositionData.PayoffSecondMortgage)
{
return LienPositionType.First;
}
else
{
return LienPositionType.Second;
}
}
return LienPositionType.First;
}
}
}
@@ -0,0 +1,45 @@
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Assets;
using Landmark.OLA.Models.Loan.Enums;
namespace CRIF.ACTion.Loan.Assets
{
public class VehicleAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public VehicleAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailVehicleSection(createApp.CrifRequestSectionNamespace, _settings);
if (createApp.LoanApplication.LoanType.Value == LoanType.V_VehiclePreapproval
&& createApp.LoanApplication.Vehicle is null)
{
createApp.LoanApplication.Vehicle = new Vehicle
{
InitialEstimatedValue = createApp.LoanApplication.RequestedAmount
};
}
((AppDetailVehicleSection)createApp.AppDetails).AddPropertyDetails(createApp.LoanApplication.Vehicle);
((AppDetailVehicleSection)createApp.AppDetails).AddSalesPriceDetails(createApp.LoanApplication);
createApp.Properties.AddProperty(new VehicleProperty(createApp.LoanApplication.Vehicle));
createApp.VehicleValuations.AddVehicleValuation(createApp.LoanApplication.Vehicle);
if (createApp.LoanApplication.LoanType == LoanType.M_Boat &&
!(createApp.LoanApplication.Vehicle.BoatAddress is null))
{
createApp.Addresses.AddAddress(createApp.LoanApplication.Vehicle.BoatAddress, ContactMechanisms.CrifContactMechanism.MarinaAddress);
}
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel);
}
}
}
@@ -0,0 +1,167 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using Landmark.OLA.Models.Assets;
using Landmark.OLA.Models.Loan.Enums;
using static CRIF.ACTion.Common.ContactMechanisms;
namespace CRIF.ACTion.Loan.Assets
{
public class VehicleProperty : IProperty
{
private readonly Vehicle _vehicle;
public VehicleProperty(Vehicle vehicle)
{
_vehicle = vehicle;
}
public XElement Build(params XElement[] content)
{
//If there's no vehicle type, it's likely a preapproval and doesn't need this element
if (_vehicle is null || _vehicle.Type is null)
return null;
var propertyValues = PropertyValues.GetPropertyValues(_vehicle.Type.Value);
var propertyElement = new XElement(CrifNamespaces.Common + propertyValues.elementName,
new XAttribute(CrifNamespaces.Common + "id", _vehicle.GetOrCreateGuid()),
new XAttribute(CrifNamespaces.Common + "propertySubType", propertyValues.PropertySubType),
new XAttribute(CrifNamespaces.Common + "yearBuilt", _vehicle.Year.Value),
new XAttribute(CrifNamespaces.Common + "initialEstimatedValue", _vehicle.InitialEstimatedValue.Value),
new XAttribute(CrifNamespaces.Common + "propertyType", propertyValues.PropertyType));
if ( _vehicle.Type != Landmark.OLA.Models.Assets.Enums.VehicleType.Automobile ) {
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "totalValue",
_vehicle.InitialEstimatedValue.Value));
}
if (_vehicle.HasClearTitle.HasValue)
{
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "hasClearTitleFL",
_vehicle.HasClearTitle.Value));
}
if (_vehicle.IsForBusinessUse.HasValue)
{
var usageType = _vehicle.IsForBusinessUse.Value ? "Business" : "Personal";
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "propertyUsageTypeID",
usageType));
}
if (_vehicle.IsOwnerOperator.HasValue)
{
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "isOwnerOperatorFL",
_vehicle.IsOwnerOperator.Value));
}
if ((_vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.PWC || _vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.Boat)
&& !string.IsNullOrWhiteSpace(_vehicle.VIN))
{
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "hullNum",
BuilderUtils.FormatCrifData(_vehicle.VIN)));
}
if (_vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.Boat && (_vehicle.BoatLength ?? 0) != 0)
{
propertyElement.Add(new XAttribute(CrifNamespaces.Common + "lengthDesc",
_vehicle.BoatLength));
}
// Order matters for elements
if (_vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.Boat
&& _vehicle.BoatAddress is not null)
{
var contactMechanisms = new XElement(CrifNamespaces.Common + "contactMechanisms");
contactMechanisms.Add(GetAddressContactMechanismElement(_vehicle.BoatAddress.Id, CrifContactMechanism.MarinaAddress));
propertyElement.Add(contactMechanisms);
}
var valuationsLinkElement = new XElement(CrifNamespaces.Common + "propertyVehicleValuations",
new XElement(CrifNamespaces.Common + "propertyVehicleValuation",
new XAttribute(CrifNamespaces.Common + "idRef", _vehicle.ValuationId)));
propertyElement.Add(valuationsLinkElement);
if (_vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.Boat)
{
propertyElement.Add(CreateMotorElement(_vehicle));
propertyElement.Add(CreateTrailerElement(_vehicle));
}
return propertyElement;
}
public static XElement CreateMotorElement(Vehicle vehicle)
{
var motorElement = new XElement(CrifNamespaces.Common + "motors");
var propertyMotorElement = new XElement(CrifNamespaces.Common + "propertyMotor",
new XAttribute(CrifNamespaces.Common + "motorNum", 1));
if (!string.IsNullOrWhiteSpace(vehicle.MotorMake))
{
propertyMotorElement.Add(new XAttribute(CrifNamespaces.Common + "make", vehicle.MotorMake));
}
if (vehicle.MotorYear is not null)
{
propertyMotorElement.Add(new XAttribute(CrifNamespaces.Common + "yearBuilt", vehicle.MotorYear.Value));
}
if (!string.IsNullOrWhiteSpace(vehicle.MotorModel))
{
propertyMotorElement.Add(new XAttribute(CrifNamespaces.Common + "model", vehicle.MotorModel));
}
if (vehicle.MotorType is not null)
{
propertyMotorElement.Add(new XAttribute(CrifNamespaces.Common + "engineTypeID", vehicle.MotorType));
}
motorElement.Add(propertyMotorElement);
return motorElement;
}
public static XElement CreateTrailerElement(Vehicle vehicle)
{
var trailerElement = new XElement(CrifNamespaces.Common + "trailer",
new XAttribute(CrifNamespaces.Common + "registrationState", "WI"));
if (!string.IsNullOrWhiteSpace(vehicle.TrailerType))
{
trailerElement.Add(new XAttribute(CrifNamespaces.Common + "propertySubType", vehicle.TrailerType));
}
if (vehicle.TrailerYear is not null)
{
trailerElement.Add(new XAttribute(CrifNamespaces.Common + "yearBuild", vehicle.TrailerYear.Value));
}
if (!string.IsNullOrWhiteSpace(vehicle.TrailerMake))
{
trailerElement.Add(new XAttribute(CrifNamespaces.Common + "make", vehicle.TrailerMake));
}
return trailerElement;
}
public static XElement GetAddressContactMechanismElement(Guid addressRef, CrifContactMechanism contactMechanismType)
{
var contactMechanism = new XElement(CrifNamespaces.Common + "contactMechanism",
new XAttribute(CrifNamespaces.Common + "contactMechanismType", contactMechanismType),
new XElement(CrifNamespaces.Common + "addressRef",
new XAttribute(CrifNamespaces.Common + "idRef", addressRef)
),
new XElement(CrifNamespaces.Common + "contactMechanismCategory",
new XAttribute(CrifNamespaces.Common + "CD", 2)
)
);
return contactMechanism;
}
}
}
@@ -0,0 +1,79 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Assets;
namespace CRIF.ACTion.Loan.Assets
{
public class VehicleValuationsSection : CrifRequestSection
{
public VehicleValuationsSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "vehicleValuations", settings)
{
}
public void AddVehicleValuation(Vehicle vehicle)
{
//If there's no vehicle type, it's likely a preapproval and doesn't need this element
if (vehicle is null || vehicle.Type is null)
return;
var valuationElement = new XElement(CrifNamespaces.Common + "vehicleValuation",
new XAttribute(CrifNamespaces.Common + "id", vehicle.ValuationId),
new XAttribute(CrifNamespaces.Common + "valuationType", "Manual"),
new XAttribute(CrifNamespaces.Common + "make", BuilderUtils.FormatCrifData(vehicle.Make)),
new XAttribute(CrifNamespaces.Common + "model", BuilderUtils.FormatCrifData(vehicle.Model)),
new XAttribute(CrifNamespaces.Common + "year", vehicle.Year.Value),
new XAttribute(CrifNamespaces.Common + "newUsed", vehicle.Condition.Value.ToString()),
new XAttribute(CrifNamespaces.Common + "estimatedValue", vehicle.InitialEstimatedValue));
var valuationValueTypesElement = new XElement(CrifNamespaces.Common + "vehicleValuationValueTypes");
valuationValueTypesElement.Add(new XElement(CrifNamespaces.Common + "vehicleValuationValueType",
new XAttribute(CrifNamespaces.Common + "valueType", "Manual"),
new XAttribute(CrifNamespaces.Common + "baseValue", vehicle.InitialEstimatedValue)
));
valuationElement.Add(valuationValueTypesElement);
if (vehicle.Mileage.HasValue)
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "mileage", vehicle.Mileage.Value));
}
if (!string.IsNullOrWhiteSpace(vehicle.Color))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "color", BuilderUtils.FormatCrifData(vehicle.Color)));
}
if (!string.IsNullOrWhiteSpace(vehicle.Body))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "body", BuilderUtils.FormatCrifData(vehicle.Body)));
}
if (!string.IsNullOrWhiteSpace(vehicle.Series))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "series", BuilderUtils.FormatCrifData(vehicle.Series)));
}
if (!string.IsNullOrWhiteSpace(vehicle.Style))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "style", BuilderUtils.FormatCrifData(vehicle.Style)));
}
if (!string.IsNullOrWhiteSpace(vehicle.Trim))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "trim", BuilderUtils.FormatCrifData(vehicle.Trim)));
}
if (!string.IsNullOrWhiteSpace(vehicle.VIN))
{
if (vehicle.Type != Landmark.OLA.Models.Assets.Enums.VehicleType.PWC)
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "vin", BuilderUtils.FormatCrifData(vehicle.VIN)));
}
}
if (!string.IsNullOrWhiteSpace(vehicle.StateTitled))
{
valuationElement.Add(new XAttribute(CrifNamespaces.Common + "state", BuilderUtils.FormatCrifData(vehicle.StateTitled)));
}
AddContent(valuationElement);
}
}
}
@@ -0,0 +1,45 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Loan.Enums;
namespace CRIF.ACTion.Loan
{
public class CreditCardAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public CreditCardAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailBankcardSection(createApp.CrifRequestSectionNamespace, _settings);
var additionalContent = new List<XElement>();
if (createApp.LoanApplication.LoanType == LoanType.CC_Secure)
{
var collateralId = createApp.LoanApplication.SecuredCardCollateralId;
//per Ben V., all Secure CC loans should have a savings account collateral equal to the CC limit
createApp.Properties.AddProperty(new SavingsAccountProperty(collateralId, createApp.LoanApplication.RequestedAmount.Value));
var appDetailProperties = new XElement(CrifNamespaces.App + "appDetailProperties",
new XElement(CrifNamespaces.App + "appDetailProperty",
new XAttribute(CrifNamespaces.Common + "idRef", collateralId),
new XAttribute(CrifNamespaces.Common + "propertyType", "CD"), //from CRIF example
new XAttribute(CrifNamespaces.Common + "propertySubType", "NoneSelected"), //from CRIF example
new XAttribute(CrifNamespaces.App + "statusID", "Active"), //from CRIF example
new XAttribute(CrifNamespaces.App + "collateralTypeID", CrifConstants.CollateralIds[CrifConstants.Collateral.CDsOrSavings])));
additionalContent.Add(appDetailProperties);
}
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel, additionalContent: additionalContent.ToArray());
}
}
}
@@ -0,0 +1,35 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class DealershipRequestBuilder : BuilderBase
{
public readonly string applicationId;
public DealershipRequestBuilder(string applicationId,
CrifSettings settings)
: base(CrifNamespaces.App.Namespace, settings)
{
this.applicationId = applicationId;
}
public override XElement Build(params XElement[] content)
{
var createAppElement = GetAppElement(applicationId);
return base.Build(createAppElement);
}
public static XElement GetAppElement(string applicationId)
{
var element = new XElement(CrifNamespaces.Empty + "getApplication");
element.SetAttributeValue("applicationID", applicationId);
return element;
}
}
}
@@ -0,0 +1,96 @@
using System.Xml.Linq;
using CRIF.ACTion.Common.Parsers;
using Landmark.OLA.Models.Loan;
namespace CRIF.ACTion.Loan
{
public class DealershipResponseParser : IResponseParser<DealershipAuthRequest>
{
public DealershipAuthRequest Parse(XElement xdoc)
{
try
{
var application = xdoc.Descendants(Common.CrifNamespaces.App + "application").GetEnumerator();
application.MoveNext();
if(application.Current == null)
{
return null;
}
var applicationId = application.Current.Attribute(Common.CrifNamespaces.App + "applicationID")?.Value;
var primaryBorrower = xdoc.Descendants(Common.CrifNamespaces.App + "primaryBorrower").GetEnumerator();
primaryBorrower.MoveNext();
var primaryBorrowerRef = primaryBorrower.Current.Attribute(Common.CrifNamespaces.Common + "idRef").Value;
string applicantLastName = null;
var individual = xdoc.Descendants(Common.CrifNamespaces.Common + "individual").GetEnumerator();
while(individual.MoveNext())
{
if(individual.Current.Attribute(Common.CrifNamespaces.Common + "id").Value.Equals(primaryBorrowerRef))
{
applicantLastName = individual.Current.Attribute(Common.CrifNamespaces.Common + "lastName")?.Value;
break;
}
}
if (applicantLastName == null)
{
// Check for Company as primary applicant
var company = xdoc.Descendants(Common.CrifNamespaces.Common + "company").GetEnumerator();
while (company.MoveNext())
{
if (company.Current.Attribute(Common.CrifNamespaces.Common + "id").Value.Equals(primaryBorrowerRef))
{
applicantLastName = company.Current.Attribute(Common.CrifNamespaces.Common + "tradeName").Value;
break;
}
}
}
/*if (applicantLastName == null)
{
// Check for DBA/Trust Name as primary applicant
var company = xdoc.Descendants(Common.CrifNamespaces.Common + "company").GetEnumerator();
while (company.MoveNext())
{
if (company.Current.Attribute(Common.CrifNamespaces.Common + "id").Value.Equals(primaryBorrowerRef))
{
applicantLastName = company.Current.Attribute(Common.CrifNamespaces.Common + "code").Value;
break;
}
}
}*/
var channel = xdoc.Descendants(Common.CrifNamespaces.Common + "channel");
var channelEnum = channel.GetEnumerator();
channelEnum.MoveNext();
var dealershipCode = channelEnum.Current.Attribute(Common.CrifNamespaces.Common + "code")?.Value;
var dealershipName = channelEnum.Current.Attribute(Common.CrifNamespaces.Common + "dbaName")?.Value;
var dealershipPassword = channelEnum.Current.Attribute(Common.CrifNamespaces.Common + "costCenterCode")?.Value;
var isActiveFlag = channelEnum.Current.Attribute(Common.CrifNamespaces.Common + "isActiveFL")?.Value;
if(isActiveFlag == "false")
{
return null;
}
var classificationItems = channel.Descendants(Common.CrifNamespaces.Common + "channelClassificationItem").GetEnumerator();
while (classificationItems.MoveNext())
{
if(string.Equals(classificationItems.Current.Attribute(Common.CrifNamespaces.Common + "name").Value, "Dealer", StringComparison.OrdinalIgnoreCase))
{
return new DealershipAuthRequest(dealershipCode, dealershipName, dealershipPassword, applicantLastName, applicationId);
}
}
return null;
}
catch
{
throw new FormatException("CRIF XML is in an unexpected layout.");
}
}
}
}
@@ -0,0 +1,28 @@
using CRIF.ACTion.Common;
using CRIF.ACTion.Configuration;
using CRIF.ACTion.Loan.Assets;
namespace CRIF.ACTion.Loan
{
public class HelocFlexAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public HelocFlexAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailHelocSection(createApp.CrifRequestSectionNamespace, _settings);
((AppDetailHelocSection)createApp.AppDetails).AddPropertyDetails(createApp.LoanApplication.RealEstate);
((AppDetailHelocSection)createApp.AppDetails).AddHmdaDisclosed(createApp.LoanApplication.PrimaryBorrower.HmdaValues.HmdaNoticeDisclosed);
createApp.Properties.AddProperty(new RealEstateProperty(createApp.LoanApplication.RealEstate));
createApp.Addresses.AddAddress(createApp.LoanApplication.RealEstate.RealEstateAddress, ContactMechanisms.CrifContactMechanism.RealEstateAddress);
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel);
}
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan
{
public interface IAppDetails
{
AppDetailsSection AppDetails { get; }
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan
{
public interface IAppNotes
{
AppNotesSectionBuilder AppNotes { get; }
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan
{
public interface ILoanApplicationRequestBuilder
{
void SetDetails(LoanApplicationRequestBuilder createApp);
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan
{
public interface IOrg
{
OrgSection Org { get; }
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan
{
public interface IProperties
{
PropertiesSection Properties { get; }
}
}
@@ -0,0 +1,8 @@
using CRIF.ACTion.Common.Builders;
namespace CRIF.ACTion.Loan
{
public interface IProperty : IBuildable
{
}
}
@@ -0,0 +1,65 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models.Addresses;
using Landmark.OLA.Models.Individuals;
namespace CRIF.ACTion.Loan.Individuals
{
public class AddIndividualRequestBuilder : BuilderBase, IAppParties, IParties, IAddresses
{
private readonly IApplicant Individual;
private readonly string ApplicationId;
public AppPartiesSection AppParties { get; set; }
public PartiesSection Parties { get; set; }
public AddressesSection Addresses { get; set; }
public AddIndividualRequestBuilder(string applicationId, IApplicant individual, CrifSettings settings)
: base(CrifNamespaces.Empty, settings)
{
Individual = individual;
ApplicationId = applicationId;
AppParties = new AppPartiesSection(CrifRequestSectionNamespace, _settings);
Parties = new PartiesSection(CrifRequestSectionNamespace, _settings);
Addresses = new AddressesSection(CrifRequestSectionNamespace, _settings);
}
public override XElement Build(params XElement[] content)
{
Parties.AddApplicant(Individual);
AppParties.AddIndividualParty(Individual, AppPartiesSection.PartyType.borrower, Individual.HasOptedOutTCPA);
Addresses.AddAddress(Individual.Address, ContactMechanisms.CrifContactMechanism.CurrentAddress);
if (Individual.MailingAddress is not null)
{
Addresses.AddAddress(Individual.MailingAddress, ContactMechanisms.CrifContactMechanism.MailingAddress);
}
if (Individual.PreviousAddresses?.Count > 0)
{
foreach (Address previousAddress in Individual.PreviousAddresses)
{
Addresses.AddAddress(previousAddress, ContactMechanisms.CrifContactMechanism.PreviousAddress);
}
}
var addIndividualElement = new XElement(CrifNamespaces.Empty + "addIndividual",
new XAttribute("applicationID", ApplicationId),
AppParties.Build(),
Parties.Build(),
Addresses.Build());
var baseElement = base.Build(addIndividualElement);
return baseElement;
}
}
}
@@ -0,0 +1,42 @@
using System.Xml.Linq;
using System.Xml.XPath;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Parsers;
using Landmark.OLA.Models.Individuals;
namespace CRIF.ACTion.Loan.Individuals
{
public class AddIndividualResponseParser: IResponseParser<Individual>
{
public Individual Parse(XElement xElement)
{
CheckErrors(xElement);
return ParseIndividual(xElement);
}
private static void CheckErrors(XElement xElement)
{
var error = xElement.XPathSelectElement("/actionAPI/addIndividual/errors/error",
CrifNamespaces.GetNamespaceManager(xElement));
if (error != null)
{
throw new Exception(error.Value);
}
}
public static Individual ParseIndividual(XElement xElement)
{
var individual = xElement.XPathSelectElement("/actionAPI/addIndividual/msg:appImportResponse/msg:applications/app:application/app:parties/common:individual",
CrifNamespaces.GetNamespaceManager(xElement));
var parsedIndividual = new Individual
{
FirstName = individual.Attribute(CrifNamespaces.Common + "firstName").Value
};
return parsedIndividual;
}
}
}
@@ -0,0 +1,68 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models;
using Landmark.OLA.Models.Addresses;
using Landmark.OLA.Models.Residences;
using static CRIF.ACTion.Common.ContactMechanisms;
namespace CRIF.ACTion.Loan.Individuals
{
public class AddressesSection : CrifRequestSection
{
public AddressesSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "addresses", settings)
{
}
public void AddAddress(IAddress address, CrifContactMechanism contactMechanismType)
{
ModelValidator.Validate(address);
var isCurrentAddress = contactMechanismType == CrifContactMechanism.CurrentAddress
? true : false;
var addressElement = new XElement(CrifNamespaces.Common + "address",
new XAttribute(CrifNamespaces.Common + "id", address.Id),
new XAttribute(CrifNamespaces.Common + "street1", BuilderUtils.FormatCrifData(address.Address1)),
new XAttribute(CrifNamespaces.Common + "cityName", BuilderUtils.FormatCrifData(address.City)),
new XAttribute(CrifNamespaces.Common + "state", address.State),
new XAttribute(CrifNamespaces.Common + "postalCode", address.PostalCode),
new XAttribute(CrifNamespaces.Common + "country", address.Country));
var addressType = address.GetType();
if (addressType.Equals(typeof(ResidenceAddress)) || addressType.IsSubclassOf(typeof(ResidenceAddress)))
{
var residence = (ResidenceAddress)address;
addressElement.Add(
new XAttribute(CrifNamespaces.Common + "isCurrentResFL", isCurrentAddress),
new XAttribute(CrifNamespaces.Common + "monthsResided", residence.MonthsResided));
var typeCode = CrifResidenceType.GetCrifResidenceTypeCode(residence.ResidenceType.Value);
if (typeCode != null)
{
var residenceTypeElement = new XElement(CrifNamespaces.Common + "residenceType",
new XAttribute(CrifNamespaces.Common + "CD", typeCode));
addressElement.Add(residenceTypeElement);
}
}
if (!string.IsNullOrWhiteSpace(address.Address2))
{
addressElement.Add(new XAttribute(CrifNamespaces.Common + "street2", BuilderUtils.FormatCrifData(address.Address2)));
}
if (!string.IsNullOrWhiteSpace(address.PostalCodeExt))
{
addressElement.Add(new XAttribute(CrifNamespaces.Common + "zipExt", address.PostalCodeExt));
}
AddContent(addressElement);
}
}
}
@@ -0,0 +1,111 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using Landmark.OLA.Models;
namespace CRIF.ACTion.Loan.Individuals
{
public class AppPartiesSection : CrifRequestSection
{
public AppPartiesSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "appParties", settings)
{
}
public void AddIndividualParty(IReferenceable individual, PartyType partyType, bool hasOptedOutTCPA = false)
{
var borrowerTypeString = partyType == PartyType.primaryBorrower ? "primaryBorrower" : "borrower";
var individualParty = new XElement(CrifNamespaces.App + borrowerTypeString,
new XAttribute(CrifNamespaces.Common + "idRef", individual.Id),
new XAttribute(CrifNamespaces.Common + "partyType", "Individual"),
new XAttribute(CrifNamespaces.App + "isCollateralGrantorFL", false)
);
individualParty.Add(GetCustomField(partyType, hasOptedOutTCPA));
AddContent(individualParty);
}
private XElement GetTCPACustomField()
{
var customField = GetBoolFieldLinkElement(CrifConstants.TCPA_Opt_Out_Field_Id,
true, "IndividualApplicant");
return customField;
}
private XElement GetCustomField(PartyType type, bool includeTCPAOptOut)
{
var fieldValue = type switch
{
PartyType.NonSigningSpouseMarriedToCoBorrower => CrifConstants.Borrower_IsApplicantNonSigningSpouse_Field_NonSigningMarriedToCo,
PartyType.NonSigningSpouseMarriedToPrimary => CrifConstants.Borrower_IsApplicantNonSigningSpouse_Field_NonSigningMarriedToPrimary,
_ => CrifConstants.Borrower_IsApplicantNonSigningSpouse_Field_Borrower,
};
var customField = GetLookupFieldLinkElement(CrifConstants.Borrower_IsApplicantNonSigningSpouse_Field_Id,
fieldValue, "IndividualApplicant");
if (includeTCPAOptOut)
{
var tcpaField = GetTCPACustomField();
var fieldsElementWithTCPA = new XElement(CrifNamespaces.App + "fields", customField, tcpaField);
return fieldsElementWithTCPA;
}
var fieldsElement = new XElement(CrifNamespaces.App + "fields", customField);
return fieldsElement;
}
public void SetSourceChannel(Guid channelId)
{
var sourceChannel = new XElement(CrifNamespaces.App + "sourceChannel",
new XAttribute(CrifNamespaces.Common + "idRef", channelId),
new XAttribute(CrifNamespaces.Common + "partyType", "Channel"));
AddContent(sourceChannel);
}
public void SetServicingChannel(Guid channelId)
{
var servicingChannel = new XElement(CrifNamespaces.App + "servicingChannel",
new XAttribute(CrifNamespaces.Common + "idRef", channelId),
new XAttribute(CrifNamespaces.Common + "partyType", "Channel"));
AddContent(servicingChannel);
}
private static XElement GetLookupFieldLinkElement(int Id, int value, string fieldLevel)
{
var lookupFieldLinkElement = new XElement(CrifNamespaces.Common + "lookupFieldLink",
new XAttribute(CrifNamespaces.Common + "value", value),
new XElement(CrifNamespaces.Common + "customLookupField",
new XAttribute(CrifNamespaces.Common + "ID", Id),
new XAttribute(CrifNamespaces.Common + "fieldLevel", fieldLevel)));
return lookupFieldLinkElement;
}
private static XElement GetBoolFieldLinkElement(int Id, bool value, string fieldLevel)
{
var lookupFieldLinkElement = new XElement(CrifNamespaces.Common + "boolFieldLink",
new XAttribute(CrifNamespaces.Common + "value", value),
new XElement(CrifNamespaces.Common + "customLookupField",
new XAttribute(CrifNamespaces.Common + "ID", Id),
new XAttribute(CrifNamespaces.Common + "fieldLevel", fieldLevel)));
return lookupFieldLinkElement;
}
public enum PartyType
{
primaryBorrower,
borrower,
NonSigningSpouseMarriedToPrimary,
NonSigningSpouseMarriedToCoBorrower,
}
}
}
@@ -0,0 +1,29 @@
using Landmark.OLA.Models.Addresses.Enums;
namespace CRIF.ACTion.Loan.Individuals
{
public static class CrifResidenceType
{
public static string GetCrifResidenceTypeCode(ResidenceTypeOption residenceTypeOption)
{
return residenceTypeOption switch
{
ResidenceTypeOption.Own => "N",
ResidenceTypeOption.Rent => "R",
ResidenceTypeOption.Other => "O",
_ => throw new ArgumentOutOfRangeException(nameof(residenceTypeOption), "Unexpected ResidenceTypeOptions value"),
};
}
public static string GetCrifResidenceTypeName(ResidenceTypeOption residenceTypeOption)
{
return residenceTypeOption switch
{
ResidenceTypeOption.Own => "Own",
ResidenceTypeOption.Rent => "Rent",
ResidenceTypeOption.Other => "Other",
_ => throw new ArgumentOutOfRangeException(nameof(residenceTypeOption), "Unexpected ResidenceTypeOptions value"),
};
}
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan.Individuals
{
public interface IAddresses
{
AddressesSection Addresses { get; }
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan.Individuals
{
public interface IAppParties
{
AppPartiesSection AppParties { get; }
}
}
@@ -0,0 +1,7 @@
namespace CRIF.ACTion.Loan.Individuals
{
public interface IParties
{
PartiesSection Parties { get; }
}
}
@@ -0,0 +1,36 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using Landmark.OLA.Models.Individuals;
namespace CRIF.ACTion.Loan.Individuals
{
public class IncomeSectionBuilder
{
private readonly Income _income;
public IncomeSectionBuilder(Income income)
{
_income = income;
}
public XElement Build()
{
var incomeElement = new XElement(CrifNamespaces.Common + "income",
new XAttribute(CrifNamespaces.Common + "IncomeTypeCD", _income.IncomeTypeCD),
new XAttribute(CrifNamespaces.Common + "frequency", _income.Frequency),
new XAttribute(CrifNamespaces.Common + "amountPerPeriod", _income.AmountPerPeriod),
new XAttribute(CrifNamespaces.Common + "incomeStatus", _income.IncomeStatus),
new XAttribute(CrifNamespaces.Common + "incomeAdjustmentPercentage", 1m),
new XAttribute(CrifNamespaces.Common + "adjustedIncome", _income.AmountPerPeriod),
new XAttribute(CrifNamespaces.Common + "sourceName", BuilderUtils.FormatCrifData(_income.SourceName)));
if (_income.HoursPerWeek.HasValue)
{
incomeElement.Add(new XAttribute(CrifNamespaces.Common + "hoursPerWeek", _income.HoursPerWeek.Value));
}
return incomeElement;
}
}
}
@@ -0,0 +1,29 @@
namespace CRIF.ACTion.Loan.Individuals
{
public static class MaritalStatus
{
public static string GetMaritalStatusCode(bool isMarried)
{
if (isMarried)
{
return "M";
}
else
{
return "U";
}
}
public static string GetMaritalStatusName(bool isMarried)
{
if (isMarried)
{
return "Married";
}
else
{
return "Unmarried";
}
}
}
}
@@ -0,0 +1,788 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using CRIF.ACTion.Services.Landmark.DistributionChannel;
using CRIF.ACTion.Services.Landmark.SourceChannel;
using Landmark.OLA.Models;
using Landmark.OLA.Models.Addresses;
using Landmark.OLA.Models.Individuals;
using Landmark.OLA.Models.Individuals.Enums;
using Landmark.OLA.Models.Loan.Enums;
using static CRIF.ACTion.Common.ContactMechanisms;
namespace CRIF.ACTion.Loan.Individuals
{
public class PartiesSection : CrifRequestSection
{
private enum CrifGender
{
NoneSelected,
Female,
NotProvided,
Male,
NotApplicable,
MaleFemale,
}
private readonly IDistributionChannelLookupService _distributionChannelLookupService;
public PartiesSection(XNamespace sectionNamespace,
CrifSettings settings,
IDistributionChannelLookupService distributionChannelLookupService = null)
: base(sectionNamespace, "parties", settings)
{
_distributionChannelLookupService = distributionChannelLookupService ??
new LandmarkDistributionChannelLookupService(settings);
}
public void AddNonApplicant(INonApplicant individual, bool isMarried = false)
{
var individualElement = GetNonApplicantElement(individual, isMarried);
AddContent(individualElement);
}
public void AddApplicant(IApplicant individual,
Guid? externalSpouseId = null,
PropertyLot mfgHomeLot = null)
{
XElement partyRelationships = null;
if (externalSpouseId.HasValue ||
individual.HasAnyNonApplicantSpouse() ||
individual.Employers?.Count > 0)
{
partyRelationships = GetApplicantPartyRelationshipsSectionElement(individual, externalSpouseId);
}
var individualElement = GetInitialIndividualElement(individual, partyRelationships, mfgHomeLot);
SetHmdaData(individual, individualElement);
//this must occur last within the individual element
var maritalStatusElement = GetMaritalStatusElement(individual.IsMarried ?? false);
individualElement.Add(maritalStatusElement);
AddContent(individualElement);
}
public void AddHistory(IIndividualHistory individual, bool isMarried = false, bool requiresHMDA = false)
{
XElement partyRelationships = null;
if (individual.Employers?.Count > 0)
{
partyRelationships = GetHistoryPartyRelationshipsSectionElement(individual);
}
var individualElement = GetInitialIndividualElement(individual, partyRelationships);
if (requiresHMDA)
{
AddNonApplicantHmdaDetermination(individualElement);
}
//this must occur last within the individual element
var maritalStatusElement = GetMaritalStatusElement(isMarried);
individualElement.Add(maritalStatusElement);
AddContent(individualElement);
}
public static void AddNonApplicantHmdaDetermination(XElement individualElement)
{
var ethnicityDeterminationElement = new XElement(CrifNamespaces.Common + "ethnicityDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "T"),
new XAttribute(CrifNamespaces.Common + "name", "No Co-Applicant"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(ethnicityDeterminationElement);
var raceDeterminationElement = new XElement(CrifNamespaces.Common + "raceDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "T"),
new XAttribute(CrifNamespaces.Common + "name", "No Co-Applicant"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(raceDeterminationElement);
var sexDeterminationElement = new XElement(CrifNamespaces.Common + "sexDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "T"),
new XAttribute(CrifNamespaces.Common + "name", "No Co-Applicant"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(sexDeterminationElement);
var applicationTakenMethodElement = new XElement(CrifNamespaces.Common + "applicationTakenMethod",
new XAttribute(CrifNamespaces.Common + "lookupType", "ApplicationTakenMethod"),
new XAttribute(CrifNamespaces.Common + "CD", "2"),
new XAttribute(CrifNamespaces.Common + "name", "Internet"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "description", "Internet"));
individualElement.Add(applicationTakenMethodElement);
}
private XElement GetNonApplicantElement(INonApplicant individual, bool isMarried = false)
{
var individualElement = new XElement(CrifNamespaces.Common + "individual",
new XAttribute(CrifNamespaces.Common + "id", individual.Id),
new XAttribute(CrifNamespaces.Common + "languageID", individual.Language.Value),
new XAttribute(CrifNamespaces.Common + "firstName", BuilderUtils.FormatCrifData(individual.FirstName)),
new XAttribute(CrifNamespaces.Common + "lastName", BuilderUtils.FormatCrifData(individual.LastName)),
new XAttribute(CrifNamespaces.Common + "ssn", individual.SSN ?? "")
);
if (!string.IsNullOrWhiteSpace(individual.MiddleInitial))
{
individualElement.Add(new XAttribute(CrifNamespaces.Common + "middleName", BuilderUtils.FormatCrifData(individual.MiddleInitial)));
}
var contact = new XElement(CrifNamespaces.Common + "contactMechanisms");
var currentAddress = GetAddressContactMechanismElement(individual.MailingAddress.Id, CrifContactMechanism.CurrentAddress);
contact.Add(currentAddress);
individualElement.Add(contact);
//this must occur last within the individual element
var maritalStatusElement = GetMaritalStatusElement(isMarried);
individualElement.Add(maritalStatusElement);
return individualElement;
}
private XElement GetInitialIndividualElement(IIndividualHistory individual,
XElement partyRelationshipElement = null,
PropertyLot mfgHomeLot = null)
{
ModelValidator.Validate(individual);
var individualElement = new XElement(CrifNamespaces.Common + "individual",
new XAttribute(CrifNamespaces.Common + "id", individual.Id),
new XAttribute(CrifNamespaces.Common + "languageID", individual.Language.Value),
new XAttribute(CrifNamespaces.Common + "firstName", BuilderUtils.FormatCrifData(individual.FirstName)),
new XAttribute(CrifNamespaces.Common + "lastName", BuilderUtils.FormatCrifData(individual.LastName)),
new XAttribute(CrifNamespaces.Common + "dob", Dates.GetFormattedDOB(individual.DOB.Value)),
new XAttribute(CrifNamespaces.Common + "ssn", individual.SSN),
new XAttribute(CrifNamespaces.Common + "customerNum", individual.CustomerNumber)
);
if (individual.IsLandmarkEmployee.HasValue)
{
individualElement.Add(new XAttribute(CrifNamespaces.Common + "isEmployeeFL", individual.IsLandmarkEmployee.Value));
}
var contact = GetContactMechanismsSectionElement(individual);
individualElement.Add(contact);
individualElement.Add(partyRelationshipElement); //order matters for this element
if (individual.Employers?.Count > 0 || individual.OtherIncomes?.Count > 0)
{
var incomes = GetIncomesSectionElement(individual);
individualElement.Add(incomes);;
}
var tradesElement = GetTradesElement(individual, mfgHomeLot);
individualElement.Add(tradesElement);
if (individual.Suffix != null)
{
var suffixElement = GetSuffixElement(individual);
individualElement.Add(suffixElement);
}
if (!string.IsNullOrWhiteSpace(individual.MiddleInitial))
{
individualElement.Add(new XAttribute(CrifNamespaces.Common + "middleName", BuilderUtils.FormatCrifData(individual.MiddleInitial)));
}
return individualElement;
}
private void SetHmdaData(IApplicant individual, XElement individualElement)
{
var racesDetermination = false;
if (individual.HmdaValues.Races != null && individual.HmdaValues.Races.Count > 0)
{
var racesElement = GetRacesElement(individual);
individualElement.Add(racesElement);
racesDetermination = true;
}
var ethnicitiesDetermination = false;
if (individual.HmdaValues.Ethnicities != null && individual.HmdaValues.Ethnicities.Count > 0)
{
var ethnicitiesElement = GetEthnicitiesElement(individual);
individualElement.Add(ethnicitiesElement);
ethnicitiesDetermination = true;
}
if (ethnicitiesDetermination)
{
if (!(individual.HmdaValues.Ethnicities.Count == 1 && individual.HmdaValues.Ethnicities.Contains(Ethnicity.NotProvided)))
{
var ethnicityDeterminationElement = new XElement(CrifNamespaces.Common + "ethnicityDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "N"),
new XAttribute(CrifNamespaces.Common + "name", "Not Collected on the Basis of Visual Observation or Surname"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(ethnicityDeterminationElement);
}
}
if (racesDetermination)
{
if (!(individual.HmdaValues.Races.Count == 1 && individual.HmdaValues.Races.Contains(Race.NotProvided)))
{
var raceDeterminationElement = new XElement(CrifNamespaces.Common + "raceDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "N"),
new XAttribute(CrifNamespaces.Common + "name", "Not Collected on the Basis of Visual Observation or Surname"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(raceDeterminationElement);
}
}
if (individual.HmdaValues.Sexes != null && individual.HmdaValues.Sexes.Count > 0)
{
var sexElements = GetSexAttributes(individual);
individualElement.Add(sexElements);
if (!(individual.HmdaValues.Sexes.Count == 1 && individual.HmdaValues.Sexes.Contains(Sex.NotProvided)))
{
var sexDeterminationElement = new XElement(CrifNamespaces.Common + "sexDetermination",
new XAttribute(CrifNamespaces.Common + "lookupType", "GMIDetermination"),
new XAttribute(CrifNamespaces.Common + "CD", "N"),
new XAttribute(CrifNamespaces.Common + "name", "Not Collected on the Basis of Visual Observation or Surname"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "externalSystemCode", "3"));
individualElement.Add(sexDeterminationElement);
}
}
var applicationTakenMethodElement = GetApplicationTakenMethodElement(individual);
individualElement.Add(applicationTakenMethodElement);
}
public XElement GetApplicationTakenMethodElement(IApplicant individual)
{
if (!GetApplicationTakenMethodElementRequired(individual))
{
return null;
}
var applicationTakenMethodElement = new XElement(CrifNamespaces.Common + "applicationTakenMethod",
new XAttribute(CrifNamespaces.Common + "lookupType", "ApplicationTakenMethod"),
new XAttribute(CrifNamespaces.Common + "CD", "2"),
new XAttribute(CrifNamespaces.Common + "name", "Internet"),
new XAttribute(CrifNamespaces.Common + "isActive", true),
new XAttribute(CrifNamespaces.Common + "description", "Internet"));
return applicationTakenMethodElement;
}
public static XElement GetSuffixElement(IIndividualHistory individual)
{
var suffixType = SuffixMap.GetSuffixType(individual.Suffix);
var suffixElement = new XElement(CrifNamespaces.Common + "suffix",
new XAttribute(CrifNamespaces.Common + "lookupType", "Suffix"),
new XAttribute(CrifNamespaces.Common + "CD", suffixType.CD),
new XAttribute(CrifNamespaces.Common + "name", suffixType.Name),
new XAttribute(CrifNamespaces.Common + "isActive", true));
return suffixElement;
}
public static XElement GetTradesElement(IIndividualHistory individual, PropertyLot mfgHomeLot)
{
var tradesElement = new XElement(CrifNamespaces.Common + "trades");
var mortgageOrRentTradeElement = new XElement(CrifNamespaces.Common + "trade",
new XAttribute(CrifNamespaces.Common + "accountTypeCD", "RealEstateMortgage"),
new XAttribute(CrifNamespaces.Common + "tradeTypeID", "Mortgage"),
new XAttribute(CrifNamespaces.Common + "creditorName", "Rent - Mortgage"),
new XAttribute(CrifNamespaces.Common + "debtStatusCD", "Include"),
new XAttribute(CrifNamespaces.Common + "ecoaCD", "Individual"),
new XAttribute(CrifNamespaces.Common + "isCurrResidenceRentMortgage", true),
new XAttribute(CrifNamespaces.Common + "monthlyPayment", individual.Address.MonthlyPayment),
new XElement(CrifNamespaces.Common + "kindOfBusiness",
new XAttribute(CrifNamespaces.Common + "lookupType", "KOB"),
new XAttribute(CrifNamespaces.Common + "CD", "FM")));
tradesElement.Add(mortgageOrRentTradeElement);
if ((!(mfgHomeLot is null)) && mfgHomeLot.RentalAmt.HasValue)
{
var mfgHomeLotRentalElement = new XElement(CrifNamespaces.Common + "trade",
new XAttribute(CrifNamespaces.Common + "tradeTypeID", "Mortgage"),
new XAttribute(CrifNamespaces.Common + "creditorName", "Lot Rent"),
new XAttribute(CrifNamespaces.Common + "debtStatusCD", "Include"),
new XAttribute(CrifNamespaces.Common + "ecoaCD", "Individual"),
new XAttribute(CrifNamespaces.Common + "monthlyPayment", mfgHomeLot.RentalAmt.Value),
new XElement(CrifNamespaces.Common + "kindOfBusiness",
new XAttribute(CrifNamespaces.Common + "lookupType", "KOB"),
new XAttribute(CrifNamespaces.Common + "CD", "FM")));
tradesElement.Add(mfgHomeLotRentalElement);
}
if (individual.OtherIncomes?.Count > 0)
{
foreach (var income in individual.OtherIncomes)
{
if (income.IncomeTypeCD == IncomeType.Alimony)
{
double monthlyAlimony = (double)income.AmountPerPeriod / 12;
var alimonyElement = new XElement(CrifNamespaces.Common + "trade",
new XAttribute(CrifNamespaces.Common + "tradeTypeID", "Open"),
new XAttribute(CrifNamespaces.Common + "creditorName", "Alimony/Child Support - Payment"),
new XAttribute(CrifNamespaces.Common + "debtStatusCD", "Include"),
new XAttribute(CrifNamespaces.Common + "ecoaCD", "Individual"),
new XAttribute(CrifNamespaces.Common + "monthlyPayment", monthlyAlimony),
new XElement(CrifNamespaces.Common + "kindOfBusiness",
new XAttribute(CrifNamespaces.Common + "lookupType", "KOB"),
new XAttribute(CrifNamespaces.Common + "CD", "VK")));
tradesElement.Add(alimonyElement);
}
}
}
return tradesElement;
}
private static bool GetApplicationTakenMethodElementRequired(IApplicant individual)
{
var hasRaces = individual.HmdaValues.Races != null && individual.HmdaValues.Races.Count > 0;
var hasEthnicities = individual.HmdaValues.Ethnicities != null && individual.HmdaValues.Ethnicities.Count > 0;
var hasSexes = individual.HmdaValues.Sexes != null && individual.HmdaValues.Sexes.Count > 0;
return hasRaces || hasEthnicities || hasSexes;
}
public static XAttribute[] GetSexAttributes(IApplicant individual)
{
var attributeList = new List<XAttribute>();
var hasMale = individual.HmdaValues.Sexes.Contains(Sex.Male);
var hasFemale = individual.HmdaValues.Sexes.Contains(Sex.Female);
var isNotProvided = individual.HmdaValues.Sexes.Contains(Sex.NotProvided);
var genderCDValue = CrifGender.NoneSelected;
if (isNotProvided && !hasMale && !hasFemale)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "genderCD", Sex.NotProvided));
return [.. attributeList];
}
if (hasMale)
{
if (hasFemale)
{
genderCDValue = CrifGender.MaleFemale;
}
else
{
genderCDValue = CrifGender.Male;
}
}
else if (hasFemale)
{
genderCDValue = CrifGender.Female;
}
if (genderCDValue != CrifGender.NoneSelected)
{
attributeList.Add(new XAttribute(CrifNamespaces.Common + "genderCD", genderCDValue.ToString()));
}
//Yes, you both could provide an answer and say that you don't wish to provide an answer
//Validation will likely restrict that
//Ignore Not Provided if Other Values are Selected
attributeList.Add(new XAttribute(CrifNamespaces.Common + "refusedToFurnishGenderFL", false));
return [.. attributeList];
}
public static XElement GetRacesElement(IApplicant individual)
{
var racesElement = new XElement(CrifNamespaces.Common + "races");
if (individual.HmdaValues.Races.Count == 1 && individual.HmdaValues.Races.Contains(Race.NotProvided)){
var raceElement = new XElement(CrifNamespaces.Common + "race",
new XAttribute(CrifNamespaces.Common + "isCheckedFL", true),
new XAttribute(CrifNamespaces.Common + "race", Race.NotProvided.ToString()));
racesElement.RemoveNodes();
racesElement.Add(raceElement);
return racesElement;
}
foreach (Race raceOption in (Race[])Enum.GetValues(typeof(Race)))
{
var isChecked = individual.HmdaValues.Races.Contains(raceOption);
bool isNotProvided = (raceOption == Race.NotProvided);
string otherRaceInfoValue = null;
switch (raceOption)
{
case Race.AmericanIndianAlaskaNative:
otherRaceInfoValue = individual.HmdaValues.AmericanIndianAlaskaNativeTribe;
break;
case Race.OtherAsian:
otherRaceInfoValue = individual.HmdaValues.OtherAsianRace;
break;
case Race.OtherPacificIslander:
otherRaceInfoValue = individual.HmdaValues.OtherPacificIslanderRace;
break;
}
var hasOtherValue = !string.IsNullOrWhiteSpace(otherRaceInfoValue);
//Ignore not provided if it is not the only ethnicity selected
if ( (isChecked || hasOtherValue) && !isNotProvided)
{
var raceElement = new XElement(CrifNamespaces.Common + "race",
new XAttribute(CrifNamespaces.Common + "isCheckedFL", isChecked),
new XAttribute(CrifNamespaces.Common + "race", raceOption.ToString()));
if (hasOtherValue)
{
raceElement.Add(new XAttribute(CrifNamespaces.Common + "otherRaceInfo", BuilderUtils.FormatCrifData(otherRaceInfoValue)));
}
racesElement.Add(raceElement);
}
}
return racesElement;
}
public static XElement GetEthnicitiesElement(IApplicant individual)
{
var ethnicitiesElement = new XElement(CrifNamespaces.Common + "ethnicities");
if (individual.HmdaValues.Ethnicities.Count == 1 && individual.HmdaValues.Ethnicities.Contains(Ethnicity.NotProvided)){
var ethnicityElement = new XElement(CrifNamespaces.Common + "ethnicity",
new XAttribute(CrifNamespaces.Common + "isCheckedFL", true),
new XAttribute(CrifNamespaces.Common + "ethnicity", Ethnicity.NotProvided.ToString()));
ethnicitiesElement.RemoveNodes();
ethnicitiesElement.Add(ethnicityElement);
return ethnicitiesElement;
}
foreach (Ethnicity ethnicityOption in (Ethnicity[])Enum.GetValues(typeof(Ethnicity)))
{
var isChecked = individual.HmdaValues.Ethnicities.Contains(ethnicityOption);
bool isNotProvided = (ethnicityOption == Ethnicity.NotProvided);
string otherEthnicityInfo = null;
switch (ethnicityOption)
{
case Ethnicity.OtherHispanicLatino:
otherEthnicityInfo = individual.HmdaValues.OtherHispanicLatinoEthnicity;
break;
}
var hasOtherValue = !string.IsNullOrWhiteSpace(otherEthnicityInfo);
//Ignore not provided if it is not the only ethnicity selected
if ( (isChecked || hasOtherValue) && !isNotProvided)
{
var ethnicityElement = new XElement(CrifNamespaces.Common + "ethnicity",
new XAttribute(CrifNamespaces.Common + "isCheckedFL", isChecked),
new XAttribute(CrifNamespaces.Common + "ethnicity", ethnicityOption.ToString()));
if (hasOtherValue)
{
ethnicityElement.Add(new XAttribute(CrifNamespaces.Common + "otherEthnicityInfo", BuilderUtils.FormatCrifData(otherEthnicityInfo)));
}
ethnicitiesElement.Add(ethnicityElement);
}
}
return ethnicitiesElement;
}
public static XElement GetIncomesSectionElement(IIndividualHistory individual)
{
var incomesElement = new XElement(CrifNamespaces.Common + "incomes");
if (individual.Employers?.Count > 0)
{
foreach (var employer in individual.Employers)
{
var income = employer.Income;
ModelValidator.Validate(income);
var incomeElement = new IncomeSectionBuilder(income).Build();
var employerRefElement = new XElement(CrifNamespaces.Common + "employerRef",
new XAttribute(CrifNamespaces.Common + "idRef", employer.GetOrCreateGuid()));
incomeElement.Add(employerRefElement);
incomesElement.Add(incomeElement);
}
}
if (individual.OtherIncomes?.Count > 0)
{
foreach (var income in individual.OtherIncomes)
{
if (income.IncomeTypeCD != IncomeType.Alimony)
{
var incomeElement = new IncomeSectionBuilder(income).Build();
incomesElement.Add(incomeElement);
}
}
}
return incomesElement;
}
public static XElement GetHistoryPartyRelationshipsSectionElement(IIndividualHistory individual)
{
var prElement = new XElement(CrifNamespaces.Common + "partyRelationships");
if (individual.Employers?.Count > 0)
{
foreach (var employer in individual.Employers)
{
ModelValidator.Validate(employer);
var employmentType = employer.EmploymentType;
var employmentElement = new XElement(CrifNamespaces.Common + "employment",
new XAttribute(CrifNamespaces.Common + "relationshipType", "Employment"));
var partyRefElement = new XElement(CrifNamespaces.Common + "partyRef",
new XAttribute(CrifNamespaces.XSI + "type", $"{CrifNamespaces.Common.Prefix}:EmployerRef"),
new XAttribute(CrifNamespaces.Common + "idRef", employer.GetOrCreateGuid()),
new XAttribute(CrifNamespaces.Common + "partyType", "Employer"));
var employmentSubRelElement = new XElement(CrifNamespaces.Common + "employmentSubRel",
new XAttribute(CrifNamespaces.Common + "isCurrentFL", employer.CurrentEmployer),
new XAttribute(CrifNamespaces.Common + "title", BuilderUtils.FormatCrifData(employer.Title)),
new XAttribute(CrifNamespaces.Common + "monthsEmployed", employer.MonthsEmployed),
new XAttribute(CrifNamespaces.Common + "isSelfEmployed", false), //TODO #239: is this always false?
new XAttribute(CrifNamespaces.Common + "employmentType", employmentType));
var userEmploymentTypeElement = new XElement(CrifNamespaces.Common + "userEmploymentType",
new XAttribute(CrifNamespaces.Common + "lookupType", "UserEmploymentType"), //TODO #240: Does this have other values?
new XAttribute(CrifNamespaces.Common + "CD", UserEmploymentType.GetCrifUserEmploymentTypeIndex(employmentType)), //TODO #240
new XAttribute(CrifNamespaces.Common + "name", UserEmploymentType.GetCrifUserEmploymentTypeName(employmentType)));
employmentSubRelElement.Add(userEmploymentTypeElement);
employmentElement.Add(partyRefElement);
employmentElement.Add(employmentSubRelElement);
prElement.Add(employmentElement);
}
}
return prElement;
}
public static XElement GetApplicantPartyRelationshipsSectionElement(IApplicant individual,
Guid? externalSpouseId = null)
{
var prElement = GetHistoryPartyRelationshipsSectionElement(individual);
prElement.Add(GetMarriageElement(individual, externalSpouseId));
return prElement;
}
private static XElement GetMarriageElement(IApplicant individual, Guid? externalSpouseId = null)
{
if (!(individual.HasAnyNonApplicantSpouse() || externalSpouseId.HasValue))
{
return null;
}
Guid spouseId;
if (externalSpouseId.HasValue)
{
spouseId = externalSpouseId.Value;
}
else
{
if (individual.NonApplicantSpouse is not null)
{
spouseId = individual.NonApplicantSpouse.Id;
}
else
{
spouseId = individual.DetailedNonApplicantSpouse.Id;
}
}
var partyRef = new XElement(CrifNamespaces.Common + "partyRef",
new XAttribute(CrifNamespaces.XSI + "type", $"{CrifNamespaces.Common.Prefix}:IndividualRef"),
new XAttribute(CrifNamespaces.Common + "idRef", spouseId),
new XAttribute(CrifNamespaces.Common + "partyType", "Individual"));
var marriageRelationshipElement = new XElement(CrifNamespaces.Common + "partyRelationship",
new XAttribute(CrifNamespaces.Common + "relationshipType", "Marriage"),
partyRef);
return marriageRelationshipElement;
}
public static XElement GetMaritalStatusElement(bool isMarried)
{
var maritalStatusElement = new XElement(CrifNamespaces.Common + "maritalStatus",
new XAttribute(CrifNamespaces.Common + "lookupType", "MaritalStatus"),
new XAttribute(CrifNamespaces.Common + "CD", MaritalStatus.GetMaritalStatusCode(isMarried)),
new XAttribute(CrifNamespaces.Common + "name", MaritalStatus.GetMaritalStatusName(isMarried)));
return maritalStatusElement;
}
public static XElement GetContactMechanismsSectionElement(IIndividualHistory individual)
{
var contact = new XElement(CrifNamespaces.Common + "contactMechanisms");
var mechanismType = ContactMechanisms.GetPhoneType(individual.PhoneType.Value);
var primaryPhone = GetContactMechanismElement(individual.Phone, mechanismType);
var email = GetContactMechanismElement(individual.Email, ContactMechanisms.CrifContactMechanism.EMail);
var currentAddress = GetAddressContactMechanismElement(individual.Address.Id, ContactMechanisms.CrifContactMechanism.CurrentAddress);
contact.Add(primaryPhone);
contact.Add(email);
contact.Add(currentAddress);
if (individual.SecondaryPhone?.Length > 0)
{
mechanismType = ContactMechanisms.GetPhoneType(individual.SecondaryPhoneType.Value);
var secondaryPhone = GetContactMechanismElement(individual.SecondaryPhone, mechanismType);
contact.Add(secondaryPhone);
}
if (individual.MailingAddress is not null)
{
var mailingAddressElement = GetAddressContactMechanismElement(individual.MailingAddress.Id, ContactMechanisms.CrifContactMechanism.MailingAddress);
contact.Add(mailingAddressElement);
}
else
{
var mailingAddressElement = GetAddressContactMechanismElement(individual.Address.Id,ContactMechanisms.CrifContactMechanism.MailingAddress);
contact.Add(mailingAddressElement);
}
if (individual.PreviousAddresses?.Count > 0)
{
foreach (var previousAddress in individual.PreviousAddresses)
{
var previousAddressElement = GetAddressContactMechanismElement(previousAddress.Id, ContactMechanisms.CrifContactMechanism.PreviousAddress);
contact.Add(previousAddressElement);
}
}
return contact;
}
public static XElement GetAddressContactMechanismElement(Guid addressRef, ContactMechanisms.CrifContactMechanism contactMechanismType)
{
var contactMechanismCategory = ContactMechanisms.GetContactMechanismCategory(contactMechanismType);
var contactMechanism = new XElement(CrifNamespaces.Common + "contactMechanism",
new XAttribute(CrifNamespaces.Common + "contactMechanismType", contactMechanismType),
new XElement(CrifNamespaces.Common + "addressRef",
new XAttribute(CrifNamespaces.Common + "idRef", addressRef)
),
new XElement(CrifNamespaces.Common + "contactMechanismCategory",
new XAttribute(CrifNamespaces.Common + "CD", (int)contactMechanismCategory)
)
);
return contactMechanism;
}
public static XElement GetEmployerContactMechanismsSectionElement(Employer employer)
{
var contact = new XElement(CrifNamespaces.Common + "contactMechanisms");
if (!string.IsNullOrWhiteSpace(employer.WorkPhone))
{
contact.Add(GetContactMechanismElement(employer.WorkPhone, CrifContactMechanism.WorkPhone));
}
return contact;
}
public static XElement GetContactMechanismElement(string contactString, CrifContactMechanism contactMechanismType)
{
var contactMechanismCategory = GetContactMechanismCategory(contactMechanismType);
var contactMechanism = new XElement(CrifNamespaces.Common + "contactMechanism",
new XAttribute(CrifNamespaces.Common + "contactMechanismType", contactMechanismType),
new XAttribute(CrifNamespaces.Common + "contactString", BuilderUtils.FormatCrifData(contactString)),
new XElement(CrifNamespaces.Common + "contactMechanismCategory",
new XAttribute(CrifNamespaces.Common + "CD", (int)contactMechanismCategory)
)
);
return contactMechanism;
}
public void AddOrganizationParty(Guid orgId)
{
var organization = new XElement(CrifNamespaces.Common + "organization",
new XAttribute(CrifNamespaces.Common + "id", orgId),
new XAttribute(CrifNamespaces.Common + "partyID", 4)); //Per CRIF Documentation
AddContent(organization);
}
public void AddSourceChannelParty(Guid channelId, LoanType loanType)
{
var channelLookup = new LandmarkSourceChannelLookupService(_settings);
int partyId = channelLookup.GetSourceChannelByLoanType(loanType);
AddChannelParty(channelId, partyId);
}
public void AddServicingChannelParty(Guid channelId, string zipCode)
{
var partyId = _distributionChannelLookupService.GetDistributionChannelByZipCode(zipCode);
AddChannelParty(channelId, partyId);
}
private void AddChannelParty(Guid channelId, int partyId)
{
var channel = new XElement(CrifNamespaces.Common + "channel",
new XAttribute(CrifNamespaces.Common + "id", channelId),
new XAttribute(CrifNamespaces.Common + "partyID", partyId));
AddContent(channel);
}
public void AddEmployer(Employer employer)
{
ModelValidator.Validate(employer);
var employerElement = new XElement(CrifNamespaces.Common + "employer",
new XAttribute(CrifNamespaces.Common + "id", employer.GetOrCreateGuid()),
new XAttribute(CrifNamespaces.Common + "name", BuilderUtils.FormatCrifData(employer.Name)),
new XAttribute(CrifNamespaces.Common + "languageID", employer.Language));
var contact = GetEmployerContactMechanismsSectionElement(employer);
employerElement.Add(contact);
AddContent(employerElement);
}
}
}
@@ -0,0 +1,38 @@
using Landmark.OLA.Models.Individuals.Enums;
namespace CRIF.ACTion.Loan.Individuals
{
public static class UserEmploymentType
{
public static string GetCrifUserEmploymentTypeIndex(EmploymentTypeOption userEmploymentTypeOption)
{
return userEmploymentTypeOption switch
{
EmploymentTypeOption.NoneSelected => "0",
EmploymentTypeOption.Contract => "1",
EmploymentTypeOption.FullTime => "2",
EmploymentTypeOption.PartTime => "3",
EmploymentTypeOption.SelfEmployed => "4",
EmploymentTypeOption.Retired => "5",
EmploymentTypeOption.Temporary => "6",
_ => throw new ArgumentOutOfRangeException(nameof(userEmploymentTypeOption), "Unexpected UserEmploymentType value"),
};
}
public static string GetCrifUserEmploymentTypeName(EmploymentTypeOption userEmploymentTypeOption)
{
return userEmploymentTypeOption switch
{
EmploymentTypeOption.NoneSelected => "None Selected",
EmploymentTypeOption.Contract => "Contract",
EmploymentTypeOption.FullTime => "Full Time",
EmploymentTypeOption.PartTime => "Part Time",
EmploymentTypeOption.SelfEmployed => "Self Employed",
EmploymentTypeOption.Retired => "Retired",
EmploymentTypeOption.Temporary => "Temporary",
_ => throw new ArgumentOutOfRangeException(nameof(userEmploymentTypeOption), "Unexpected UserEmploymentType value"),
};
}
}
}
@@ -0,0 +1,384 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
using CRIF.ACTion.Services.Landmark.DistributionChannel;
using CRIF.ACTion.Loan.Assets;
using CRIF.ACTion.Loan.Individuals;
using Landmark.OLA.Models.Addresses;
using Landmark.OLA.Models.Individuals;
using Landmark.OLA.Models.Loan;
using Landmark.OLA.Models.Loan.Enums;
namespace CRIF.ACTion.Loan
{
public class LoanApplicationRequestBuilder : BuilderBase, IParties, IAppDetails, IAppParties,
IAddresses, IOrg, IProperties, IVehicleValuations, IAppNotes
{
public readonly LoanApplication LoanApplication;
private readonly Guid OrgRef;
public readonly Guid _sourceChannel;
public readonly Guid _servicingChannel;
public AppPartiesSection AppParties { get; set; }
public PartiesSection Parties { get; set; }
public AppDetailsSection AppDetails { get; set; }
public AddressesSection Addresses { get; set; }
public AppNotesSectionBuilder AppNotes { get; set; }
public OrgSection Org { get; set; }
public PropertiesSection Properties { get; set; }
public VehicleValuationsSection VehicleValuations { get; set; }
public LoanApplicationRequestBuilder(LoanApplication loanApplication,
CrifSettings settings,
Guid? orgRef = null,
Guid? sourceChannel = null,
Guid? servicingChannel = null,
IDistributionChannelLookupService distributionChannelLookupService = null)
: base(CrifNamespaces.App.Namespace, settings)
{
LoanApplication = loanApplication;
OrgRef = orgRef ?? Guid.NewGuid();
_sourceChannel = sourceChannel ?? Guid.NewGuid();
_servicingChannel = servicingChannel ?? Guid.NewGuid();
AppParties = new AppPartiesSection(CrifRequestSectionNamespace, settings);
Parties = new PartiesSection(CrifRequestSectionNamespace, settings, distributionChannelLookupService);
Addresses = new AddressesSection(CrifRequestSectionNamespace, settings);
AppNotes = new AppNotesSectionBuilder(CrifRequestSectionNamespace, settings);
Org = new OrgSection(CrifRequestSectionNamespace, settings);
Properties = new PropertiesSection(CrifRequestSectionNamespace, settings);
VehicleValuations = new VehicleValuationsSection(CrifRequestSectionNamespace, settings);
}
public override XElement Build(params XElement[] content)
{
Org.SetOrgId(OrgRef);
Parties.AddOrganizationParty(OrgRef);
Parties.AddSourceChannelParty(_sourceChannel, LoanApplication.LoanType.Value);
Parties.AddServicingChannelParty(_servicingChannel, LoanApplication?.PrimaryBorrower?.Address?.PostalCode ?? "");
AppParties.SetSourceChannel(_sourceChannel);
AppParties.SetServicingChannel(_servicingChannel);
AddApplicationNotes();
var productDetails = CreateProductDetails(LoanApplication.LoanType, _settings);
productDetails.SetDetails(this);
Guid? externalSpouseId = null;
if (LoanApplication.CoApplicant is not null && LoanApplication.CoApplicant.IsMarriedToPrimaryBorrower)
{
externalSpouseId = LoanApplication.CoApplicant.Id;
}
PropertyLot mfgHomeLot = null;
if (!(LoanApplication.ManufacturedHome?.Lot?.RentalAmt is null))
{
//needed to pass long possible Lot rent amount
mfgHomeLot = LoanApplication.ManufacturedHome.Lot;
}
ProcessIndividual(LoanApplication.PrimaryBorrower,
AppPartiesSection.PartyType.primaryBorrower,
externalSpouseId,
mfgHomeLot);
if (LoanApplication.CoApplicant is not null)
{
ProcessIndividual(LoanApplication.CoApplicant, AppPartiesSection.PartyType.borrower);
CheckForAndProcessSpouses(LoanApplication.CoApplicant);
}
CheckForAndProcessSpouses(LoanApplication.PrimaryBorrower, true);
var createAppElement = GetCreateAppElement(LoanApplication.Date,
AppParties.Build(),
Parties.Build(),
AppDetails.Build(),
Properties.Build(),
Addresses.Build(),
AppNotes.Build(),
Org.Build(),
VehicleValuations.Build());
var baseElement = base.Build(createAppElement);
return baseElement;
}
private void AddApplicationNotes()
{
AppNotes.AddAppNote("IP: " + LoanApplication.IPAddress + ", DISCLOSURE DATE: " + ConvertUtcToCentral(LoanApplication.DisclosureDate));
if (!string.IsNullOrWhiteSpace(LoanApplication.PrimaryContactMethod))
{
AppNotes.AddAppNote("PRIMARY CONTACT METHOD: " + LoanApplication.PrimaryContactMethod);
}
if (!string.IsNullOrWhiteSpace(LoanApplication.PromoCode))
{
AppNotes.AddAppNote("PROMO CODE: " + LoanApplication.PromoCode);
}
if (!string.IsNullOrWhiteSpace(LoanApplication.Note))
{
AppNotes.AddAppNote(LoanApplication.Note);
}
if (LoanApplication.RealEstate is not null && LoanApplication.RealEstate.LienPosition is not null &&
LoanApplication.LoanTitle != "Heloc Rate Modification")
{
if (LoanApplication.RealEstate.LienPosition.PayoffFirstMortgage)
{
AppNotes.AddAppNote("I INTEND TO PAYOFF MY FIRST MORTGAGE WITH THIS LOAN.");
}
if (LoanApplication.RealEstate.LienPosition.PayoffSecondMortgage)
{
AppNotes.AddAppNote("I INTEND TO PAYOFF MY SECOND MORTGAGE WITH THIS LOAN.");
}
}
if (LoanApplication.LoanType == LoanType.S_Special)
{
AppNotes.AddAppNote("Special Loan: " + LoanApplication.LoanTitle);
AppNotes.AddAppNote("Client Message: " + LoanApplication.ClientMessage);
}
if (LoanApplication.LoanType == LoanType.S_Signature && !string.IsNullOrWhiteSpace(LoanApplication.PurposeOfLoanSignature))
{
AppNotes.AddAppNote("PURPOSE OF LOAN: " + LoanApplication.PurposeOfLoanSignature);
}
if (LoanApplication.LoanType == LoanType.EQ_HELOC_Flex) {
if (!string.IsNullOrWhiteSpace(LoanApplication.ModFeeAccount))
{
AppNotes.AddAppNote("MOD FEE ACCOUNT: " + LoanApplication.ModFeeAccount);
}
if (!string.IsNullOrWhiteSpace(LoanApplication.PrimaryBorrower.PrimaryApplicantEmail))
{
AppNotes.AddAppNote("PRIMARY APPLICANT EMAIL (E-DOC SIGNATURE): " + LoanApplication.PrimaryBorrower.PrimaryApplicantEmail);
}
if (!string.IsNullOrWhiteSpace(LoanApplication.PrimaryBorrower.CoApplicantEmail))
{
AppNotes.AddAppNote("CO-APPLICANT EMAIL (E-DOC SIGNATURE): " + LoanApplication.PrimaryBorrower.CoApplicantEmail);
}
}
if (LoanApplication.Vehicle != null)
{
if (LoanApplication.Vehicle.Type == Landmark.OLA.Models.Assets.Enums.VehicleType.Motorcycle
&& !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.StateTitled))
{
AppNotes.AddAppNote("State of Registration: " + LoanApplication.Vehicle.StateTitled);
}
if (LoanApplication.Vehicle.Lien == "yes")
{
AppNotes.AddAppNote("Vehicle has a lien");
}
var seller = LoanApplication.Vehicle?.Seller;
if (seller != null)
{
var sellerStr = "INTERESTED IN BUYING FROM A " + seller.Value;
sellerStr = sellerStr.Replace('_', ' ');
if (!string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerName) || !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerPhone))
{
bool bothSeller = !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerName) && !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerPhone);
sellerStr += ":";
sellerStr += !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerName) ? " " + LoanApplication.Vehicle.DealerName : "";
sellerStr += bothSeller ? " - " : "";
sellerStr += !string.IsNullOrWhiteSpace(LoanApplication.Vehicle.DealerPhone) ? " " + String.Format("{0:(###) ###-####}", LoanApplication.Vehicle.DealerPhone) : "";
}
AppNotes.AddAppNote(sellerStr);
}
}
}
private void CheckForAndProcessSpouses(IApplicant individual, bool isPrimaryBorrowerSpouse = false)
{
var partyType = isPrimaryBorrowerSpouse ? AppPartiesSection.PartyType.NonSigningSpouseMarriedToPrimary
: AppPartiesSection.PartyType.NonSigningSpouseMarriedToCoBorrower;
//Validation can esnure that only one of these holds data
if (individual.NonApplicantSpouse is not null)
{
ProcessIndividual(individual.NonApplicantSpouse, partyType);
}
if (individual.DetailedNonApplicantSpouse is not null)
{
ProcessIndividual(individual.DetailedNonApplicantSpouse, partyType);
}
}
private void ProcessIndividual(INonApplicant individual, AppPartiesSection.PartyType partyType)
{
var isMarried = partyType == AppPartiesSection.PartyType.NonSigningSpouseMarriedToCoBorrower
|| partyType == AppPartiesSection.PartyType.NonSigningSpouseMarriedToPrimary;
Parties.AddNonApplicant(individual, isMarried);
Addresses.AddAddress(individual.MailingAddress,
ContactMechanisms.CrifContactMechanism.CurrentAddress);
}
private void ProcessIndividual(IApplicant individual,
AppPartiesSection.PartyType partyType,
Guid? externalSpouseId = null,
PropertyLot mfgHomeLot = null)
{
Parties.AddApplicant(individual, externalSpouseId, mfgHomeLot);
ProcessHistory(individual, partyType);
}
private void ProcessIndividual(IIndividualHistory individual, AppPartiesSection.PartyType partyType)
{
var isMarried = partyType == AppPartiesSection.PartyType.NonSigningSpouseMarriedToCoBorrower
|| partyType == AppPartiesSection.PartyType.NonSigningSpouseMarriedToPrimary;
bool requiresHMDA = IsHMDARequired(LoanApplication.LoanType.ToString());
Parties.AddHistory(individual, isMarried, requiresHMDA);
ProcessHistory(individual, partyType);
}
public static bool IsHMDARequired(string loanType)
{
HMDALoanType parsedLoanType;
if (Enum.TryParse(loanType, out parsedLoanType))
{
return true;
}
else
{
return false;
}
}
private void ProcessHistory(IIndividualHistory individual, AppPartiesSection.PartyType partyType)
{
AddHistory(individual, partyType);
if (individual.Employers?.Count > 0)
{
foreach (var employer in individual.Employers)
{
Parties.AddEmployer(employer);
}
}
}
public static XElement GetCreateAppElement(DateTime date, params XElement[] content)
{
var element = new XElement(CrifNamespaces.Empty + "createApp",
new XElement(CrifNamespaces.App + "application",
new XAttribute(CrifNamespaces.App + "appReceivedDate",
Dates.GetFormattedApplicationDate(date)),
new XAttribute(CrifNamespaces.App + "appInitiatedDate",
Dates.GetFormattedApplicationDate(date)),
new XAttribute(CrifNamespaces.App + "priorityCD", "High"), //Per vendor examples
content));
return element;
}
private void AddHistory(IIndividualHistory individual, AppPartiesSection.PartyType partyType)
{
AppParties.AddIndividualParty(individual, partyType, individual.HasOptedOutTCPA);
Addresses.AddAddress(individual.Address,
ContactMechanisms.CrifContactMechanism.CurrentAddress);
if (!(individual.MailingAddress is null))
{
Addresses.AddAddress(individual.MailingAddress, ContactMechanisms.CrifContactMechanism.MailingAddress);
}
if (individual.PreviousAddresses?.Count > 0)
{
foreach (var previousAddress in individual.PreviousAddresses)
{
Addresses.AddAddress(previousAddress, ContactMechanisms.CrifContactMechanism.PreviousAddress);
}
}
}
private ILoanApplicationRequestBuilder CreateProductDetails(LoanType? loanType, CrifSettings settings)
{
if (!loanType.HasValue)
throw new ArgumentException("LoanType must have a value.");
if (settings == null)
throw new ArgumentException("Product Details requires CrifSettings");
ILoanApplicationRequestBuilder appBuilder = null;
switch (loanType.Value)
{
case LoanType.CC_CashBack:
case LoanType.CC_Rewards:
case LoanType.CC_Student:
case LoanType.CC_Secure:
appBuilder = new CreditCardAppBuilder(settings);
break;
case LoanType.EQ_HELOC_Flex:
appBuilder = new HelocFlexAppBuilder(settings);
break;
case LoanType.EQ_Mtg_Refi_Express:
case LoanType.EQ_Home_Equity_Fixed:
appBuilder = new RealEstateEquityAppBuilder(settings);
break;
case LoanType.EQ_Mfg_Home_With_or_Without_Land:
appBuilder = new MfgHousingAndLandAppBuilder(settings);
break;
case LoanType.V_VehiclePreapproval:
case LoanType.V_Vehicle:
case LoanType.M_Boat:
case LoanType.P_Powersport:
case LoanType.T_TrailerMotorhome:
appBuilder = new VehicleAppBuilder(settings);
break;
case LoanType.S_Special:
case LoanType.S_Signature:
appBuilder = new SignatureAppBuilder(settings);
break;
default:
throw new ArgumentOutOfRangeException(nameof(loanType), $"The requested loan type {loanType.Value} is not implemented.");
}
return appBuilder;
}
private static string ConvertUtcToCentral(DateTime date)
{
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime centralDate = TimeZoneInfo.ConvertTimeFromUtc(date.ToUniversalTime(), timeZone);
return centralDate.ToString("g");
}
}
}
@@ -0,0 +1,25 @@
using System.Xml.Linq;
using CRIF.ACTion.Common.Parsers;
using Landmark.OLA.Models.Loan;
namespace CRIF.ACTion.Loan
{
public class LoanApplicationResponseParser : IResponseParser<LoanApplication>
{
public LoanApplication Parse(XElement xdoc)
{
try
{
//TODO: This entire parser should be rewritten to use proper XPath-style lookups
var idElement = (XElement)((XElement)((XElement)((XElement)xdoc.FirstNode).FirstNode).FirstNode).NextNode;
return new LoanApplication(loanId: idElement.Value);
}
catch
{
throw new FormatException("CRIF XML is in an unexpected layout.");
}
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class OrgSection : CrifRequestSection
{
public OrgSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "org", settings)
{
}
public void SetOrgId(Guid orgId)
{
var orgAttribute = new XAttribute(CrifNamespaces.Common + "idRef", orgId);
AddContent(orgAttribute);
}
}
}
@@ -0,0 +1,19 @@
using System.Xml.Linq;
using CRIF.ACTion.Common.Builders;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class PropertiesSection : CrifRequestSection
{
public PropertiesSection(XNamespace sectionNamespace, CrifSettings settings)
: base(sectionNamespace, "properties", settings)
{
}
public void AddProperty(IProperty property)
{
AddContent(property.Build());
}
}
}
@@ -0,0 +1,29 @@
using System.Xml.Linq;
using CRIF.ACTion.Common;
namespace CRIF.ACTion.Loan
{
public class SavingsAccountProperty : IProperty
{
private readonly Guid _Id;
private readonly decimal _Value;
public SavingsAccountProperty(Guid id, decimal value)
{
_Id = id;
_Value = value;
}
public XElement Build(params XElement[] content)
{
var element = new XElement(CrifNamespaces.Common + "propertyStocksAndBonds",
new XAttribute(CrifNamespaces.Common + "id", _Id.ToString()),
new XAttribute(CrifNamespaces.Common + "finalEstimatedValue", _Value),
new XAttribute(CrifNamespaces.Common + "propertyType", "CD"), //from CRIF Example
new XAttribute(CrifNamespaces.Common + "description", "CDs or Savings")); //from CRIF Example
return element;
}
}
}
@@ -0,0 +1,21 @@
using System.Xml.Linq;
using CRIF.ACTion.Configuration;
namespace CRIF.ACTion.Loan
{
public class SignatureAppBuilder : ILoanApplicationRequestBuilder
{
private readonly CrifSettings _settings;
public SignatureAppBuilder(CrifSettings settings)
{
_settings = settings;
}
public void SetDetails(LoanApplicationRequestBuilder createApp)
{
createApp.AppDetails = new AppDetailSignatureSection(createApp.CrifRequestSectionNamespace, _settings);
var additionalContent = new List<XElement>();
createApp.AppDetails.SetDetails(createApp.LoanApplication,
createApp._sourceChannel, additionalContent: [.. additionalContent]);
}
}
}