403 lines
17 KiB
C#
403 lines
17 KiB
C#
using CsvHelper;
|
|
using CsvHelper.Configuration;
|
|
using System.Globalization;
|
|
using ReserveManagementSystemApi.Helpers;
|
|
using ReserveManagementSystemApi.Models;
|
|
using ReserveManagementSystemApi.ServiceClients;
|
|
using ReserveManagementSystemLib.Enums;
|
|
using ReserveManagementSystemLib.Helpers;
|
|
using ReserveManagementSystemLib.Models;
|
|
using ReserveManagementSystemLib.Configuration;
|
|
using ReserveManagementSystemApi.Enums;
|
|
using LCU.Api.Internal.MaidBotLib.Models;
|
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.IndirectLoan;
|
|
|
|
namespace ReserveManagementSystemApi.Services
|
|
{
|
|
public class ImportReadService : IImportReadService
|
|
{
|
|
private readonly ILoanService _loanService;
|
|
private readonly IImportService _importService;
|
|
private readonly IDealerService _dealerService;
|
|
private readonly IExportService _exportService;
|
|
private readonly ICoreApiServiceClient _coreApiServiceClient;
|
|
private readonly RmsApiConfiguration _rmsApiConfig;
|
|
private ImportHelper _importHelper = new ImportHelper();
|
|
|
|
public ImportReadService(ILoanService loanService,
|
|
IImportService importService,
|
|
IDealerService dealerService,
|
|
IExportService exportService,
|
|
ICoreApiServiceClient coreApiServiceClient,
|
|
RmsApiConfiguration rmsApiConfig)
|
|
{
|
|
_loanService = loanService;
|
|
_importService = importService;
|
|
_dealerService = dealerService;
|
|
_exportService = exportService;
|
|
_coreApiServiceClient = coreApiServiceClient;
|
|
_rmsApiConfig = rmsApiConfig;
|
|
}
|
|
|
|
public async Task<Import> ImportActiveLoansFromDNA(string newerThanDateTime, bool skipExport = false)
|
|
{
|
|
if (string.IsNullOrEmpty(newerThanDateTime))
|
|
{
|
|
Import lastImport = await _importService.GetLastLoanImportByType(ImportType.Active);
|
|
newerThanDateTime = GetStartDateForNextImport(lastImport);
|
|
}
|
|
Log.Information("Get Active Loans By Posted Date is starting.");
|
|
var dnaLoans = await _coreApiServiceClient.GetIndirectLoansByPostedDate(newerThanDateTime);
|
|
Log.Information($"Get Active Loans By Posted Date has ended. Found {dnaLoans.Count()} active loan records.");
|
|
|
|
Log.Information("Starting ProcessActiveLoanImport");
|
|
var import = await ProcessActiveLoanImport(dnaLoans, DateTime.Now, skipExport);
|
|
Log.Information("ProcessActiveLoanImport has ended");
|
|
|
|
return import;
|
|
}
|
|
|
|
public async Task<Import> ImportClosedLoansFromDNA(string newerThanDateTime = "")
|
|
{
|
|
if (string.IsNullOrEmpty(newerThanDateTime))
|
|
{
|
|
var lastImport = await _importService.GetLastLoanImportByType(ImportType.Closed);
|
|
newerThanDateTime = GetStartDateForNextImport(lastImport);
|
|
}
|
|
var dnaLoans = await _coreApiServiceClient.GetClosedLoansByPostedDate(newerThanDateTime);
|
|
var import = await ProcessClosedLoanImport(dnaLoans, DateTime.Now, newerThanDateTime);
|
|
|
|
return import;
|
|
}
|
|
|
|
public async Task<Import> ImportClosedLoansWithUpdatesFromDNA(string newerThanDateTime, bool skipExport)
|
|
{
|
|
var dnaLoans = await _coreApiServiceClient.GetClosedLoansWithDetailByClosedDate(newerThanDateTime);
|
|
var newImport = new Import();
|
|
|
|
try
|
|
{
|
|
newImport = await ProcessClosedLoansWithUpdates(dnaLoans, DateTime.Now, skipExport);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failure to import closed loans from DNA.");
|
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
|
Enum.GetName(typeof(GroupEnum), GroupEnum.IndirectLending))
|
|
.Error(ex, $"Reserve Management System Failure. " +
|
|
$"Error while importing Closed Loans from DNA.");
|
|
throw;
|
|
}
|
|
|
|
return newImport;
|
|
}
|
|
|
|
public async Task<Import> ProcessActiveLoanImport(List<IndirectLoanDetail> loanDetails, DateTime currentTime, bool skipExport = false)
|
|
{
|
|
var newImport = new Import
|
|
{
|
|
ImportType = ImportType.Active.ToString(),
|
|
StartDate = currentTime
|
|
};
|
|
|
|
newImport = await _importService.AddImportAsync(newImport);
|
|
|
|
try
|
|
{
|
|
var loans = _importHelper.TransformLoanFromDNA(loanDetails, newImport.ImportId); //Transform import to loan object
|
|
|
|
await _dealerService.UpdateDealersAsync(loans);
|
|
|
|
var allDealers = await _dealerService.GetAllDealersAsync();
|
|
loans.ForEach(loan =>
|
|
{
|
|
var currentDealer = allDealers.FirstOrDefault(a => a.DealerOrganizationNumber == loan.DealerOrganizationNumber);
|
|
loan.Dealer = currentDealer; // assign the loan the updated dealer then check validation in save method
|
|
});
|
|
|
|
await SaveLoansExportAndImportAsync(newImport, loans, currentTime, skipExport);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failure to import active loans from DNA.");
|
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
|
Enum.GetName(typeof(GroupEnum), GroupEnum.IndirectLending))
|
|
.Error(ex, $"Reserve Management System Failure. " +
|
|
$"Error while importing Active Loans from DNA.");
|
|
throw;
|
|
}
|
|
|
|
return newImport;
|
|
}
|
|
|
|
public async Task<Import> ProcessClosedLoansWithUpdates(List<IndirectLoanDetail> dnaLoans, DateTime currentTime, bool skipExport)
|
|
{
|
|
var closedImport = new Import
|
|
{
|
|
ImportType = ImportType.Closed.ToString(),
|
|
StartDate = currentTime
|
|
};
|
|
|
|
closedImport = await _importService.AddImportAsync(closedImport);
|
|
var loans = _importHelper.TransformLoanFromDNA(dnaLoans, closedImport.ImportId);
|
|
|
|
await _dealerService.UpdateDealersAsync(loans);
|
|
|
|
var loansClosed = await _loanService.SaveWithReserveAndChargebackAsync(loans);
|
|
|
|
if (!skipExport)
|
|
{
|
|
//prevent converted loans from exporting
|
|
DateTime defaultConvertedPostDate = Convert.ToDateTime(_rmsApiConfig.DefaultConvertedLoanPostDate);
|
|
var convertedLoansMarkExported = loansClosed.Where(loan => loan.PostedDate < defaultConvertedPostDate && loan.LoanStatus == "Closed").ToList();
|
|
await _exportService.MarkReservesAsExportedAsync(convertedLoansMarkExported, currentTime);
|
|
|
|
//update dealer original balance with only non-converted loans
|
|
await _exportService.ExportReservesAsync(DateTime.Now);
|
|
var nonConvertedLoans = loansClosed.Where(loan => loan.PostedDate > defaultConvertedPostDate).ToList();
|
|
var listAccountNumbers = nonConvertedLoans.Select(loan => loan.LoanNumber).ToList();
|
|
await _loanService.UpdateDealerOriginalBalanceAsync(listAccountNumbers);
|
|
|
|
await _exportService.ExportChargebacksAsync(DateTime.Now);
|
|
}
|
|
else
|
|
{
|
|
await _exportService.MarkReservesAndChargebacksAsExportedAsync(loansClosed, DateTime.Now);
|
|
}
|
|
|
|
closedImport.EndDate = DateTime.Now;
|
|
closedImport.RecordCount = loansClosed.Count;
|
|
closedImport.RecordsSaved = loansClosed.Count;
|
|
closedImport.Success = true;
|
|
|
|
await _importService.UpdateImportAsync(closedImport);
|
|
|
|
return closedImport;
|
|
}
|
|
|
|
public async Task<Import> ProcessClosedLoanImport(List<ClosedIndirectLoan> closedLoans, DateTime currentTime, string newerThanDateTime)
|
|
{
|
|
List<Loan> loansUpdated;
|
|
var closedImport = new Import
|
|
{
|
|
ImportType = ImportType.Closed.ToString(),
|
|
StartDate = currentTime
|
|
};
|
|
|
|
closedImport = await _importService.AddImportAsync(closedImport);
|
|
|
|
try
|
|
{
|
|
loansUpdated = await CloseLoans(closedLoans, closedImport);
|
|
var needDetails = loansUpdated.Where(l => l.LoanStatus == LoanStatus.MissingActiveLoan.ToString()).ToList();
|
|
if (needDetails.Count > 0)
|
|
{
|
|
await ImportClosedLoansWithUpdatesFromDNA(newerThanDateTime, false);
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failure to import closed loans from DNA.");
|
|
throw;
|
|
}
|
|
|
|
return closedImport;
|
|
}
|
|
|
|
public async Task<Import> ReadNewLoanImportFile(StreamReader importFile, string fileName, DateTime currentTime)
|
|
{
|
|
var loansFromImport = new List<ImportNewLoan>();
|
|
var newImport = new Import
|
|
{
|
|
FileName = fileName.Truncate(50),
|
|
StartDate = currentTime,
|
|
ImportType = ImportType.Active.ToString()
|
|
};
|
|
|
|
newImport = await _importService.AddImportAsync(newImport);
|
|
|
|
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
|
|
{
|
|
HasHeaderRecord = false,
|
|
TrimOptions = TrimOptions.Trim,
|
|
HeaderValidated = null,
|
|
MissingFieldFound = null
|
|
};
|
|
|
|
try
|
|
{
|
|
using (var csv = new CsvReader(importFile, config))
|
|
{
|
|
loansFromImport = csv.GetRecords<ImportNewLoan>().ToList();
|
|
}
|
|
|
|
var dealers = loansFromImport.Select(l => new Dealer
|
|
{
|
|
DealerOrganizationNumber = l.DealerOrganizationNumber
|
|
})
|
|
.GroupBy(d => d.DealerOrganizationNumber).First().ToList();
|
|
|
|
await _dealerService.UpdateDealersAsync(dealers);
|
|
|
|
var loans = _importHelper.TransformNewLoan(loansFromImport, newImport.ImportId); //Transform import to loan object
|
|
|
|
await SaveLoansExportAndImportAsync(newImport, loans, currentTime);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Failure to read new loan file, message: {0} {1}", ex.Message, ex.StackTrace);
|
|
throw;
|
|
}
|
|
|
|
return newImport;
|
|
}
|
|
|
|
public async Task<List<ImportBoatLoan>> ReadBoatLoanImportFile(StreamReader importFile)
|
|
{
|
|
var loansFromImport = new List<ImportBoatLoan>();
|
|
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
|
|
{
|
|
HasHeaderRecord = false,
|
|
TrimOptions = TrimOptions.Trim,
|
|
HeaderValidated = null,
|
|
MissingFieldFound = null,
|
|
BadDataFound = null
|
|
};
|
|
|
|
try
|
|
{
|
|
using (var csv = new CsvReader(importFile, config))
|
|
{
|
|
loansFromImport = csv.GetRecords<ImportBoatLoan>().ToList();
|
|
}
|
|
|
|
var loans = _importHelper.TransformBoatLoan(loansFromImport);
|
|
loansFromImport = await ExportBoatLoansAsync(loans);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failure to read boat loan file");
|
|
throw;
|
|
}
|
|
|
|
return loansFromImport;
|
|
}
|
|
|
|
public async Task<Import> ReadClosedLoanImportFile(StreamReader importFile, string fileName, DateTime currentTime)
|
|
{
|
|
var loansFromImport = new List<ClosedIndirectLoan>();
|
|
List<Loan> loansUpdated;
|
|
|
|
var closedImport = new Import
|
|
{
|
|
FileName = fileName.Truncate(50),
|
|
StartDate = currentTime,
|
|
ImportType = ImportType.Closed.ToString()
|
|
};
|
|
|
|
closedImport = await _importService.AddImportAsync(closedImport);
|
|
|
|
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
|
|
{
|
|
HasHeaderRecord = true,
|
|
TrimOptions = TrimOptions.Trim,
|
|
PrepareHeaderForMatch = args => args.Header.ToLower()
|
|
};
|
|
|
|
try
|
|
{
|
|
using (var csv = new CsvReader(importFile, config))
|
|
{
|
|
loansFromImport = csv.GetRecords<ClosedIndirectLoan>().ToList();
|
|
}
|
|
|
|
loansUpdated = await CloseLoans(loansFromImport, closedImport);
|
|
Log.Information($"Closed {loansUpdated.Count} loans");
|
|
await _exportService.ExportChargebacksAsync(DateTime.Now);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Failure to read closed loan file, message: {0} {1}", ex.Message, ex.StackTrace);
|
|
throw;
|
|
}
|
|
|
|
return closedImport;
|
|
}
|
|
|
|
private async Task<List<Loan>> CloseLoans(List<ClosedIndirectLoan> loansFromImport, Import closedImport)
|
|
{
|
|
var loans = _importHelper.TransformClosedLoans(loansFromImport, closedImport.ImportId).ToList();
|
|
var loansUpdated = await _loanService.SaveWithChargebackAsync(loans);
|
|
|
|
var missingActiveLoans = loansUpdated.Where(l => l.LoanStatus == LoanStatus.MissingActiveLoan.ToString()).ToList();
|
|
|
|
await _exportService.ExportChargebacksAsync(DateTime.Now);
|
|
|
|
closedImport.EndDate = DateTime.Now;
|
|
closedImport.RecordCount = loansFromImport.Count;
|
|
closedImport.RecordsSaved = loansUpdated.Count - missingActiveLoans.Count;
|
|
closedImport.Success = true;
|
|
|
|
await _importService.UpdateImportAsync(closedImport);
|
|
Log.Information($"Closed {loansUpdated.Count} loans");
|
|
|
|
return loansUpdated;
|
|
}
|
|
|
|
private async Task<List<ImportBoatLoan>> ExportBoatLoansAsync(List<Loan> loans, bool skipExport = false)
|
|
{
|
|
var boatResults = new List<ImportBoatLoan>();
|
|
if (!skipExport)
|
|
{
|
|
boatResults = await _exportService.ExportBoatLoansAsync(loans);
|
|
var reserveLoans = loans.Where(l => l.ReserveAmount != 0);
|
|
var listAccounts = reserveLoans.Select(loan => loan.LoanNumber).ToList();
|
|
await _loanService.UpdateDealerOriginalBalanceAsync(listAccounts);
|
|
}
|
|
|
|
Log.Information($"Imported {loans.Count} boat loans");
|
|
return boatResults;
|
|
}
|
|
|
|
private string GetStartDateForNextImport(Import lastImport)
|
|
{
|
|
if (lastImport?.StartDate != null)
|
|
{
|
|
return lastImport.StartDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
|
}
|
|
return _rmsApiConfig.DefaultImportStartDate;
|
|
}
|
|
|
|
private async Task SaveLoansExportAndImportAsync(Import import, List<Loan> loans, DateTime currentTime, bool skipExport = false)
|
|
{
|
|
var loansImported = await _loanService.SaveLoansAsync(loans);
|
|
if (!skipExport)
|
|
{
|
|
//prevent converted loans from exporting
|
|
DateTime defaultConvertedPostDate = Convert.ToDateTime(_rmsApiConfig.DefaultConvertedLoanPostDate);
|
|
var convertedLoansMarkExported = loansImported.Where(loan => loan.PostedDate < defaultConvertedPostDate && loan.LoanStatus == "New").ToList();
|
|
await _exportService.MarkReservesAsExportedAsync(convertedLoansMarkExported, currentTime);
|
|
|
|
//update dealer original balance with only non-converted loans
|
|
await _exportService.ExportReservesAsync(currentTime);
|
|
var nonConvertedLoans = loansImported.Where(loan => loan.PostedDate > defaultConvertedPostDate).ToList();
|
|
var listAccountNumbers = nonConvertedLoans.Select(loan => loan.LoanNumber).ToList();
|
|
await _loanService.UpdateDealerOriginalBalanceAsync(listAccountNumbers);
|
|
}
|
|
else
|
|
{
|
|
await _exportService.MarkReservesAsExportedAsync(loansImported, currentTime);
|
|
}
|
|
|
|
Log.Information($"Imported {loansImported.Count} active loans");
|
|
import.EndDate = DateTime.Now;
|
|
import.RecordCount = loansImported.Count;
|
|
import.RecordsSaved = loansImported.Count;
|
|
import.Success = true;
|
|
|
|
await _importService.UpdateImportAsync(import);
|
|
}
|
|
}
|
|
}
|