using CsvHelper; using CsvHelper.Configuration; using System.Globalization; using System.IO; using LCU.API.Internal.Operations.IncentiveApi.Services.MicroApiServices; using LCU.API.Internal.Operations.IncentiveApi.Models.Temenos; using LCU.API.Internal.Operations.IncentiveApi.Services; using LCU.Library.Internal.ApiResponse.Services; using LCU.Library.Internal.Exceptions; using LCU.Library.Internal.IncentiveApiModels.Requests; using LCU.Library.Internal.IncentiveApiModels.Results; using LCU.API.Internal.Operations.IncentiveApiLib.Repositories; using LCU.Library.Internal.IncentiveApiModels.Entities; using LCU.Library.Internal.IncentiveApiModels.Enums; using LCU.API.Internal.Operations.IncentiveApi.Services.Transformations; namespace LCU.API.Internal.Operations.IncentiveApi.Services.Temenos; public class ImportIndirectVehicleSalesService : IImportIndirectVehicleSalesService { private readonly IUserRepository _userRepository; private readonly IActivityService _activityService; private readonly IFileOperationsApiService _fileOperationsApiService; private readonly IDeadActivityService _deadActivityService; private readonly ProcessResult _importIndirectVehicleSalesResult; public ImportIndirectVehicleSalesService(IActivityService activityService, IUserRepository userRepository, IFileOperationsApiService fileOperationsApiService, IDeadActivityService deadActivityService) { _activityService = activityService; _userRepository = userRepository; _fileOperationsApiService = fileOperationsApiService; _deadActivityService = deadActivityService; _importIndirectVehicleSalesResult = new ProcessResult(); } public async Task HandleImportIndirectVehicleSales(ImportIndirectVehicleSalesRequest request) { try { var records = await GetIndirectVehicleRecordsForImport(request); await CreateAndSaveActivities(records); await SafeQueueReturnResponse.TryQueueResponse(request, _importIndirectVehicleSalesResult.Success, _importIndirectVehicleSalesResult.MessageStack, nameof(ImportIndirectVehicleSalesService)); } catch (Exception exception) { var message = $"There was an issue with handling Import Indirect Vehicle Sales. Exception Message: {exception.Message}."; _importIndirectVehicleSalesResult.MessageStack.Add(message); _importIndirectVehicleSalesResult.Success = false; await SafeQueueReturnResponse.TryQueueResponse(request, _importIndirectVehicleSalesResult.Success, _importIndirectVehicleSalesResult.MessageStack, nameof(ImportIndirectVehicleSalesService)); } } private async Task> GetIndirectVehicleRecordsForImport(ImportIndirectVehicleSalesRequest request) { var recordsToImport = new List(); var directDynamicFileListRequest = request.ToDirectDynamicVehicleFileListRequest(request.DirectDynamicFileName); var directFiles = await _fileOperationsApiService.BuildDynamicFileList(directDynamicFileListRequest); var directRecords = new List(); foreach (var file in directFiles) { directRecords.AddRange(await GetRecords(request.FinalBasePath + file)); } if (directRecords.Any()) { var indirectDynamicFileListRequest = request.ToIndirectDynamicVehicleFileListRequest(request.IndirectDynamicFileName); var indirectFiles = await _fileOperationsApiService.BuildDynamicFileList(indirectDynamicFileListRequest); var indirectRecords = new List(); foreach(var file in indirectFiles) { indirectRecords.AddRange(await GetRecords(request.FinalBasePath + file)); } recordsToImport = GetIndirectRecordsToImport(directRecords, indirectRecords); } return recordsToImport; } private async Task CreateAndSaveActivities(List records) { var activities = new List(); var deadActivities = new List(); foreach (var record in records) { var activity = new Activity() { Product = Product.Vehicle, Type = ActivityType.Sale, State = ActivityState.Accepted, SourceSystem = SystemType.Temenos, SourceCreated = DateTime.Parse(record.DisbursedDate), Accepted = DateTime.Parse(record.DisbursedDate).Date.AddHours(12), SourceId = record.ApplicationID, Detail = new ActivityDetail() { AccountNumber = record.AccountNumber, MemberName = record.ApplicantName, AccountBalance = !string.IsNullOrEmpty(record.AmountFinanced) ? ParseMoneyDouble(record.AmountFinanced) : ParseMoneyDouble("0.0"), AccountStatus = record.SubProduct }, EmployeeEmail = null, EmployeeId = null, DepartmentId = -1 }; var employee = await FindEmployee(record.ReferringEmployeeEmail); if (employee == null) { var message = $"Temenos Import, No user for {record.ReferringEmployeeEmail}. Saving as Dead Activity."; _importIndirectVehicleSalesResult.MessageStack.Add(message); activity.EmployeeEmail = record.ReferringEmployeeEmail; deadActivities.Add(new DeadActivity(activity, DeadActivityReason.MissingRequiredField, message)); continue; } activity.AppUserId = employee.Id; activity.EmployeeEmail = employee.AdEmail; activity.EmployeeId = employee.EmployeeId; var homeDept = employee.FindHomeDepartment(); if (homeDept == null) { var message = $"User has no reliably determinable home department, {employee.EmployeeId}. Saving as Dead Activity."; _importIndirectVehicleSalesResult.MessageStack.Add(message); deadActivities.Add(new DeadActivity(activity, DeadActivityReason.MissingRequiredField, message)); continue; } activity.DepartmentId = homeDept.DepartmentId; activities.Add(activity); } if (activities.Any()) { await SaveActivities(activities); } if (deadActivities.Any()) { await SaveDeadActivities(deadActivities); } _importIndirectVehicleSalesResult.MessageStack.Add($"Imported { activities.Count + deadActivities.Count } indirect vehicle sales."); } private async Task FindEmployee(string employeeEmail) { try { return await _userRepository.FindUserByAdEmailAsync(employeeEmail); } catch (Exception exception) { _importIndirectVehicleSalesResult.MessageStack.Add($"Error when finding user by email in ImportIndirectVehicleSalesService. Exception message: { exception.Message }"); return null; } } private async Task SaveActivities(List activities) { try { await _activityService.SaveActivityRangeAsync(activities); } catch (Exception exception) { var message = $"Failed to save activities from ImportIndirectVehicleSalesService. Activities will be lost. Eception Message: {exception.Message}"; _importIndirectVehicleSalesResult.MessageStack.Add(message); throw new ServiceException($"{message}. Exception: {exception.Message}"); } } private async Task SaveDeadActivities(List deadActivities) { try { await _deadActivityService.SaveDeadActivityRange(deadActivities); } catch (Exception exception) { var message = $"Failed to save dead activities from ImportIndirectVehicleSalesService. Dead Activities will be lost. Eception Message: {exception.Message}"; _importIndirectVehicleSalesResult.MessageStack.Add(message); throw new ServiceException($"{message}. Exception: {exception.Message}"); } } private static List GetIndirectRecordsToImport(List directRecords, List indirectRecords) { var recordsToImport = new List(); foreach (var indirectRecord in indirectRecords) { if (!recordsToImport.Any(x => x.ApplicationID == indirectRecord.ApplicationID)) { var directSsnMatches = directRecords.Where(x => x.ApplicantSSN == indirectRecord.ApplicantSSN); var directCreatedBeforeIndirectMatches = directSsnMatches.Where(x => DateTime.Parse(x.CreatedDate) < DateTime.Parse(indirectRecord.CreatedDate)); if (directCreatedBeforeIndirectMatches.Any()) { var matchedDirectRecord = directCreatedBeforeIndirectMatches.OrderByDescending(x => DateTime.Parse(x.CreatedDate)).FirstOrDefault(); indirectRecord.ReferringEmployeeEmail = matchedDirectRecord?.ReferringEmployeeEmail; recordsToImport.Add(indirectRecord); } } } return recordsToImport; } private async Task> GetRecords(string filePathAndName) { var contents = await _fileOperationsApiService.GetFileContent(filePathAndName); using var stringReader = new StringReader(contents); var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = "|", HasHeaderRecord = false }; using var csv = new CsvReader(stringReader, config); return csv.GetRecords().ToList(); } private double? ParseMoneyDouble(string amountFinanced) { var parsed = decimal.Parse(amountFinanced, NumberStyles.Currency, new CultureInfo("en-Us")); return (double)parsed; } }