266 lines
11 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|