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