Files
monorepo/LCU.API.Internal.OnlineLoanApp/CRIF.ACTion/Services/Crif/CrifVendorClient.cs
T
2026-03-11 11:18:25 -05:00

266 lines
11 KiB
C#

using Microsoft.Extensions.Options;
using Serilog;
using CRIF.ACTion.Common.Parsers;
using CRIF.ACTion.Configuration;
using CRIF.ACTion.Decisioning;
using CRIF.ACTion.Files;
using CRIF.ACTion.Loan;
using CRIF.ACTion.Loan.Individuals;
using CRIF.ACTion.Services.Landmark.DistributionChannel;
using Landmark.OLA.Models;
using Landmark.OLA.Models.Decisioning;
using Landmark.OLA.Models.Decisioning.Enums;
using Landmark.OLA.Models.Individuals;
using Landmark.OLA.Models.Loan;
namespace CRIF.ACTion.Services.Crif
{
public class CrifVendorClient : ICrifVendorClient
{
private readonly string _getCustomFieldsString = "getCustomFields";
private readonly string _getProductDetailsString = "getProducts";
private readonly int retryLimit = 6;
private readonly ICrifService _crifService;
private readonly IResponseParser<Individual> _individualResponseParser;
private readonly IResponseParser<LoanApplication> _loanApplicationResponseParser;
private readonly IResponseParser<DecisioningState> _decisioningStateResponseParser;
private readonly IResponseParser<DecisioningDetails> _decisioningDetailsResponseParser;
private readonly IResponseParser<SortedList<int, string>> _configurationResponseParser;
private readonly IResponseParser<Dictionary<string, string>> _productDetailsResponseParser;
private readonly IResponseParser<CustomFieldConfigs> _customFieldResponseParser;
private readonly IResponseParser<DealershipAuthRequest> _dealershipResponseParser;
private readonly CrifSettings _settings;
public CrifVendorClient(ICrifService crifService,
IResponseParser<Individual> individualResponseParser,
IResponseParser<LoanApplication> loanApplicationResponseParser,
IResponseParser<DecisioningState> decisioningStateResponseParser,
IResponseParser<DecisioningDetails> decisioningDetailsResponseParser,
IResponseParser<SortedList<int, string>> crifConfigurationResponseParser,
IResponseParser<Dictionary<string, string>> productDetailsResponseParser,
IResponseParser<CustomFieldConfigs> customFieldResponseParser,
IResponseParser<DealershipAuthRequest> dealershipResponseParser,
IOptions<CrifSettings> settings)
{
_crifService = crifService;
_individualResponseParser = individualResponseParser;
_loanApplicationResponseParser = loanApplicationResponseParser;
_decisioningStateResponseParser = decisioningStateResponseParser;
_decisioningDetailsResponseParser = decisioningDetailsResponseParser;
_configurationResponseParser = crifConfigurationResponseParser;
_productDetailsResponseParser = productDetailsResponseParser;
_customFieldResponseParser = customFieldResponseParser;
_dealershipResponseParser = dealershipResponseParser;
_settings = settings.Value;
_settings.CustomFields = GetCustomFieldConfiguration().GetAwaiter().GetResult();
}
public async Task<DealershipAuthRequest> GetDealershipAuthDetails(string applicationId)
{
var getApp = new DealershipRequestBuilder(applicationId, _settings);
var xmlApp = getApp.Build();
try
{
var response = await _crifService.SendMessage(xmlApp);
return _dealershipResponseParser.Parse(response);
}
catch (Exception e)
{
Log.Fatal("Error parsing response: " + e.Message);
throw;
}
}
public async Task<LoanApplication> CreateApplication(LoanApplication loanApplication, IDistributionChannelLookupService channelService = null)
{
Log.Debug("Building Loan Application");
ModelValidator.Validate(loanApplication);
var createApp = new LoanApplicationRequestBuilder(loanApplication, _settings, null, null, null, channelService);
var xmlApp = createApp.Build();
var response = await _crifService.SendMessage(xmlApp);
try
{
return _loanApplicationResponseParser.Parse(response);
}
catch (Exception)
{
Log.Fatal("Error parsing response.");
throw;
}
}
public async Task<string> CreateApplicationAndGetId(LoanApplication loanApplication, IDistributionChannelLookupService channelService = null)
{
var parsedLoanApplication = await CreateApplication(loanApplication, channelService);
return parsedLoanApplication.Id;
}
public async Task<bool> AddFile(string applicationId, LoanApplicationFile file, string fileBase64Value)
{
try
{
var addFile = new FileRequestBuilder(applicationId, file, fileBase64Value, _settings);
var response = await _crifService.SendMessage(addFile.Build());
return true;
}
catch (Exception e)
{
Log.Fatal("Error uploading file to loan application: " + applicationId);
Log.Fatal(e.ToString());
}
return false;
}
public async Task<Individual> AddIndividual(string applicationId, Individual individual)
{
ModelValidator.Validate(individual);
var addIndividual = new AddIndividualRequestBuilder(applicationId, individual, _settings);
var response = await _crifService.SendMessage(addIndividual.Build());
try
{
return _individualResponseParser.Parse(response);
}
catch (Exception)
{
Log.Fatal("Error parsing response.");
throw;
}
}
public async Task<bool> SendToDecisioning(string applicationId)
{
var sendToDecisioning = new SendToDecisioningRequestBuilder(applicationId, _settings);
var response = await _crifService.SendMessage(sendToDecisioning.Build());
//TODO: This cast is not particularly Dependency Injection-y
//TODO: Requiring the Application Id is really just a double check, and may not matter all that much
((DecisioningStateResponseParser)_decisioningStateResponseParser).SetLoanId(applicationId);
var result = _decisioningStateResponseParser.Parse(response);
if (result == DecisioningState.MissingRequiredInformation)
{
throw new InvalidOperationException("Loan application cannot be sent to decisioning, and is likely missing required information.");
}
if (result == DecisioningState.Requested || result == DecisioningState.Ready)
{
return true;
}
else
{
return false;
}
}
public async Task<DecisioningDetails> GetDecisionStatus(string applicationId)
{
//TODO: This cast is not particularly Dependency Injection-y
//TODO: Requiring the Application Id is really just a double check, and may not matter all that much
((GetApplicationProductsResponseParser)_decisioningDetailsResponseParser).SetLoanId(applicationId);
var result = new DecisioningDetails()
{
DecisioningState = DecisioningState.Unknown
};
int retryAttempts = 0;
Thread.Sleep(2500);
do
{
Thread.Sleep((retryAttempts * 200) + 1500);
var getDecisionStatus = new GetApplicationProductsRequestBuilder(applicationId, _settings);
var response = await _crifService.SendMessage(getDecisionStatus.Build());
result = _decisioningDetailsResponseParser.Parse(response);
retryAttempts++;
} while (retryAttempts < retryLimit && result.DecisioningState == DecisioningState.Unknown);
if (retryAttempts >= retryLimit && result.DecisioningState == DecisioningState.Unknown)
{
result.DecisioningState = DecisioningState.Ready;
result.DecisioningResult = DecisioningResult.DecisionStatus_Review;
}
Log.Information($"Application ID: {applicationId}, Number of Attempts to Get DecisionStatus: {retryAttempts}");
return result;
}
public async Task<SortedList<int, string>> GetBranchNameConfiguration(string ConfigurationSectionName)
{
var configurationRequest = new ConfigurationRequestBuilder(ConfigurationSectionName, _settings);
var response = await _crifService.SendMessage(configurationRequest.Build());
var result = _configurationResponseParser.Parse(response);
return result;
}
public async Task<CustomFieldConfigs> GetCustomFieldConfiguration()
{
CustomFieldConfigs customFieldConfig = new CustomFieldConfigs();
string municipalityTypeId = null;
string municipalityWhereKeptId = null;
try
{
var configurationRequest = new ConfigurationRequestBuilder(_getCustomFieldsString, _settings);
var response = await _crifService.SendMessage(configurationRequest.Build());
customFieldConfig = _customFieldResponseParser.Parse(response);
if (_settings?.CustomFieldMappings?.CustomFieldID != null)
{
_settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityType", out municipalityTypeId);
_settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityWhereKept", out municipalityWhereKeptId);
}
Log.Information(
"CRIF custom field mappings loaded: MunicipalityType={MunicipalityTypeId}, MunicipalityWhereKept={MunicipalityWhereKeptId}",
municipalityTypeId ?? "<missing>",
municipalityWhereKeptId ?? "<missing>");
}
catch (Exception ex)
{
Log.Fatal("Error parsing CRIF custom fields configuration response." + ex.StackTrace);
}
customFieldConfig.ProductIdToCategoryId = await GetProductDetailsConfiguration();
return customFieldConfig;
}
public async Task<Dictionary<string, string>> GetProductDetailsConfiguration()
{
Dictionary<string, string> mappingList = [];
try
{
var configurationRequest = new ConfigurationRequestBuilder(_getProductDetailsString, _settings);
var response = await _crifService.SendMessage(configurationRequest.Build());
mappingList = _productDetailsResponseParser.Parse(response);
}
catch (Exception ex)
{
Log.Fatal("Error parsing CRIF custom fields configuration response." + ex.StackTrace);
}
return mappingList;
}
}
}