using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Threading.Tasks; using LCU.API.Collections.RepoApi.Exceptions; using LCU.API.Collections.RepoApi.Models; using LCU.API.Collections.RepoApi.Models.Filters; using LCU.API.Collections.RepoApi.Models.Requests; using LCU.Shared.Collections.RepoApiLib.Exceptions; using LCU.Shared.Collections.RepoApiLib.Models; using LCU.Shared.Collections.RepoApiLib.Models.Producer; using LCU.Shared.Collections.RepoApiLib.Models.Results; using LCU.Shared.Collections.RepoApiLib.Repositories; using Serilog; namespace LCU.API.Collections.RepoApi.Services { public class RepoAccountService : IRepoAccountService { private readonly IRepoAccountRepository _repoAccountRepository; private readonly IRepoStatusChangeRepository _repoStatusChangeRepository; private readonly IRepoManualStatusRepository _repoManualStatusRepository; private readonly IUnitOfWork _unitOfWork; public RepoAccountService( IRepoAccountRepository repoAccountRepository, IRepoStatusChangeRepository repoStatusChangeRepository, IRepoManualStatusRepository repoManualStatusRepository, IUnitOfWork unitOfWork) { _repoAccountRepository = repoAccountRepository; _repoStatusChangeRepository = repoStatusChangeRepository; _repoManualStatusRepository = repoManualStatusRepository; _unitOfWork = unitOfWork; } public async Task> ListAll(PageFilter pageFilter, RepoAccountPostFilter repoAccountFilter) { try { return await _repoAccountRepository.ListAsync( repoAccountFilter != null ? repoAccountFilter.RepoIdList : new List(), pageFilter.Page, pageFilter.Limit, repoAccountFilter?.Status, repoAccountFilter?.AuctionHouseId, repoAccountFilter?.RepoCompanyId, repoAccountFilter?.OnlyOpenAccounts); } catch (Exception ex) { Log.Error(ex, "Failure retrieving RepoAccounts"); throw new ServerErrorException(); } } public async Task FindByAuctionAssignmentId(int auctionAssignmentId) { try { return await _repoAccountRepository.FindByAuctionAssignmentIdAsync(auctionAssignmentId); } catch (Exception ex) { Log.Error(ex, "Failure retrieving RepoAccount"); throw new ServerErrorException(); } } public async Task SaveAsync(RepoAccount repoAccount) { try { var newAccount = await _repoAccountRepository.AddAsync(repoAccount); RepoStatusChange change = new RepoStatusChange() { RepoId = newAccount.RepoId, ChangedBy = "System", StatusFrom = "None", StatusTo = repoAccount.Status, ChangedDate = DateTime.Now }; await _repoStatusChangeRepository.AddAsync(change); await _unitOfWork.CompleteAsync(); return repoAccount; } catch (Exception ex) { Log.Error(ex, $"Failure saving Repo Account: {repoAccount}", repoAccount); throw new ServerErrorException(); } } public async Task Get(int id) { RepoAccount repoAccount; try { repoAccount = await _repoAccountRepository.FindByIdAsync(id); } catch (Exception ex) { Log.Error(ex, $"Failure retrieving Repo Account with id: {id}"); throw new ServerErrorException(); } if (repoAccount == null) { throw new NotFoundException($" Repo Accounts with id {id} not found."); } return repoAccount; } public async Task> GetByAccountNo(string accountNo) { IReadOnlyCollection repoAccounts; try { repoAccounts = await _repoAccountRepository.FindByAccountNoAsync(accountNo); } catch (Exception ex) { Log.Error(ex, $"Failure retrieving Repo Account by accountNo with accountNo: {accountNo}"); throw new ServerErrorException(); } if (repoAccounts == null || !repoAccounts.Any()) { throw new NotFoundException($" Repo Accounts with id {accountNo} not found."); } return repoAccounts; } public async Task UpdateAsync(RepoAccount repoAccount, AppUser currentUser) { var existingRepoAccount = await _repoAccountRepository.FindByIdAsync(repoAccount.RepoId); if (existingRepoAccount == null) { throw new NotFoundException($"Repo Account with repoId {repoAccount.RepoId} not found."); } var originalStatus = existingRepoAccount.Status; existingRepoAccount.AccountLastProcessed = repoAccount.AccountLastProcessed; existingRepoAccount.AccountNo = repoAccount.AccountNo; existingRepoAccount.AccountTypeCode = repoAccount.AccountTypeCode; existingRepoAccount.Balance = repoAccount.Balance; existingRepoAccount.Bankruptcy = repoAccount.Bankruptcy; existingRepoAccount.Borrowers = repoAccount.Borrowers; existingRepoAccount.Branch = repoAccount.Branch; // Only one field can change in collaterals when updating the account. if (existingRepoAccount.Collaterals != null) { foreach (var collateral in existingRepoAccount.Collaterals) { var newCollateral = repoAccount.Collaterals.FirstOrDefault(x => x.CollateralId == collateral.CollateralId); if (newCollateral != null) { collateral.RetitledInLandmarkName = newCollateral.RetitledInLandmarkName; } } } // existingRepoAccount.Collaterals = repoAccount.Collaterals; This will fail, save separately. existingRepoAccount.Collector = repoAccount.Collector; existingRepoAccount.CurrentDelinquentAmount = repoAccount.CurrentDelinquentAmount; existingRepoAccount.DateClosed = repoAccount.DateClosed; existingRepoAccount.DateFirstDelinquent = repoAccount.DateFirstDelinquent; existingRepoAccount.DateFirstIntellect = repoAccount.DateFirstIntellect; existingRepoAccount.DaysDelinquent = repoAccount.DaysDelinquent; existingRepoAccount.DelinquentAmount = repoAccount.DelinquentAmount; existingRepoAccount.DueToPayDate = repoAccount.DueToPayDate; existingRepoAccount.LastPaymentDate = repoAccount.LastPaymentDate; existingRepoAccount.LoanNo = repoAccount.LoanNo; existingRepoAccount.LstPmtAmt = repoAccount.LstPmtAmt; existingRepoAccount.ManualRedemptionDate = repoAccount.ManualRedemptionDate; existingRepoAccount.MemberNo = repoAccount.MemberNo; existingRepoAccount.O4RDate = repoAccount.O4RDate; existingRepoAccount.OrigAmount = repoAccount.OrigAmount; existingRepoAccount.OrigAccountTypeCode = repoAccount.OrigAccountTypeCode; existingRepoAccount.PaidSkipAmount = repoAccount.PaidSkipAmount; existingRepoAccount.PrimaryIndividualId = repoAccount.PrimaryIndividualId; existingRepoAccount.RedemptionExpiredDate = repoAccount.RedemptionExpiredDate; existingRepoAccount.RefundStatus = repoAccount.RefundStatus; existingRepoAccount.RequiredPaymentAmount = repoAccount.RequiredPaymentAmount; existingRepoAccount.ReferenceId = repoAccount.ReferenceId; existingRepoAccount.ReferenceCaseId = repoAccount.ReferenceCaseId; existingRepoAccount.ReferenceStatus = repoAccount.ReferenceStatus; existingRepoAccount.Status = repoAccount.Status; existingRepoAccount.SubStatus = repoAccount.SubStatus; existingRepoAccount.WrittenOffDate = repoAccount.WrittenOffDate; existingRepoAccount.SixtyPlusCollectorId = repoAccount.SixtyPlusCollectorId; try { _repoAccountRepository.Update(existingRepoAccount); if (originalStatus != repoAccount.Status) { var change = new RepoStatusChange() { RepoId = repoAccount.RepoId, ChangedBy = currentUser.FirstName + " " + currentUser.LastName, StatusFrom = originalStatus, StatusTo = repoAccount.Status, ChangedDate = DateTime.Now }; await _repoStatusChangeRepository.AddAsync(change); } await _unitOfWork.CompleteAsync(); return await _repoAccountRepository.FindByIdAsync(repoAccount.RepoId); } catch (SqlException ex) { Log.Error(ex, $"SQL Failure updating Repo Account: {repoAccount}", repoAccount); throw new ServerErrorException(); } catch (Exception ex) { Log.Error(ex, $"Failure updating Repo Account: {repoAccount}", repoAccount); throw new ServerErrorException(); } } public async Task DeleteAsync(int id) { RepoAccount existingRepoAccount; try { existingRepoAccount = await _repoAccountRepository.FindByIdAsync(id); } catch(Exception ex) { Log.Error(ex, $"Error finding an existing repo to delete with account ID: {id}"); throw new ServerErrorException(); } if (existingRepoAccount == null) { throw new NotFoundException($" Repo Account with id {id} not found."); } try { _repoAccountRepository.Remove(existingRepoAccount); await _unitOfWork.CompleteAsync(); return existingRepoAccount; } catch (SqlException ex) { Log.Error(ex, $"SQL Failure deleting Repo Account with id: {id}", id); throw new ServerErrorException(); } catch (Exception ex) { Log.Error(ex, $"Failure deleting Repo Account with id: {id}", id); throw new ServerErrorException(); } } public async Task> GetItemsForProcessing() { try { var watchStatus = await _repoManualStatusRepository.ListWatchStatusesAsync(); var repoAccountNos = await _repoAccountRepository.GetItemsForProcessing(watchStatus); return repoAccountNos; } catch (Exception ex) { Log.Error(ex, "Failure getting Repo Account Nos for polling"); throw new ServerErrorException(); } } public async Task> SearchRepoAccounts(string searchPhrase, int maxAllowedResults) { try { var matchedRepoAccounts = await _repoAccountRepository.SearchRepoAccounts(searchPhrase, maxAllowedResults); return matchedRepoAccounts; } catch (MaxItemException ex) { Log.Error(ex, $"Search Phrase Returned More Items Than Max (${maxAllowedResults}) searchPhrase:{searchPhrase}", searchPhrase); throw new MaxItemException(ex.ItemCount); } catch (Exception ex) { Log.Error(ex, $"Failure Searching For Repo Accounts searchPhrase: {searchPhrase}", searchPhrase); throw new ServerErrorException(); } } public async Task GetNewestOpenAccount(string accountNo) { IReadOnlyCollection repoAccounts; try { repoAccounts = await _repoAccountRepository.FindByAccountNoAsync(accountNo); } catch (Exception ex) { Log.Error(ex, $"Failure retrieving Repo Account by accountNo with accountNo: {accountNo}"); throw new ServerErrorException(); } if (repoAccounts == null || !repoAccounts.Any()) { // You can't throw here, it's not an exception case, there simply isn't an account. (404) Log.Debug($" Repo Accounts with id {accountNo} not found."); return null; } return SelectFirstOpenAccount(repoAccounts); } /** * Attempts to select the repo account that should be updated. Since we have to go by AccountNo and there * may be more than one. Try to return an open account, and log if it detects more than one. */ public RepoAccount SelectFirstOpenAccount(IReadOnlyCollection repoAccounts) { if (repoAccounts.Count == 1) { return repoAccounts.First(); } RepoAccount openAccount = null; foreach (var repoAccount in repoAccounts.OrderBy(x => x.RepoId)) { if (repoAccount.DateClosed == null) { if (openAccount != null) { Log.Fatal($"Multiple -OPEN- Repos with AccountNo: {openAccount.AccountNo}, {openAccount.RepoId} / {repoAccount.RepoId}"); return openAccount; } openAccount = repoAccount; } } if (openAccount != null) { return openAccount; } return repoAccounts.OrderByDescending(x => x.DateClosed).ToList().FirstOrDefault(); } } }