Files
2026-03-11 11:18:25 -05:00

385 lines
16 KiB
C#

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