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 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 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 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 ProcessActiveLoanImport(List 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 ProcessClosedLoansWithUpdates(List 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 ProcessClosedLoanImport(List closedLoans, DateTime currentTime, string newerThanDateTime) { List 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 ReadNewLoanImportFile(StreamReader importFile, string fileName, DateTime currentTime) { var loansFromImport = new List(); 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().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> ReadBoatLoanImportFile(StreamReader importFile) { var loansFromImport = new List(); 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().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 ReadClosedLoanImportFile(StreamReader importFile, string fileName, DateTime currentTime) { var loansFromImport = new List(); List 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().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> CloseLoans(List 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> ExportBoatLoansAsync(List loans, bool skipExport = false) { var boatResults = new List(); 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 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); } } }