Files
monorepo/LCU.WebApps.Indirect.ReserveManagementSystem/Api/ReserveManagementSystemApi/Services/ImportReadService.cs
T
2026-03-17 08:54:34 -05:00

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