Files
2026-04-20 14:47:24 -05:00

253 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LCU.API.Collections.RepoApi.Models.Responses;
using LCU.Shared.Collections.RepoApiLib.Models;
using LCU.Shared.Collections.RepoApiLib.Repositories;
using Serilog;
namespace LCU.API.Collections.RepoApi.Services
{
public class FactoidService : IFactoidService
{
private readonly IReportingRepository _reportingRepository;
private readonly IRepoAccountRepository _repoAccountRepository;
private readonly IImsAutoFileDataRepository _imsAutoFileDataRepository;
private const string O4RStatus = "O4R";
private const string RepoStatus = "REPO";
private const string RedeemedStatus = "REDEEMED";
public FactoidService(
IReportingRepository reportingRepository,
IRepoAccountRepository repoAccountRepository,
IImsAutoFileDataRepository imsAutoFileDataRepository)
{
_reportingRepository = reportingRepository;
_repoAccountRepository = repoAccountRepository;
_imsAutoFileDataRepository = imsAutoFileDataRepository;
}
public int FindAuctionIndex(IReadOnlyCollection<string> auctionHouses, string auctionId)
{
if (auctionId != null)
{
foreach (var item in auctionHouses.Select((value, index) => new {index, value}))
{
if (item.value == auctionId.Trim().ToLower())
{
return item.index;
}
}
}
return auctionHouses.Count - 1;
}
public async Task<FactoidResponse> GetCurrentInventory()
{
var accountsInRepo = await _repoAccountRepository.FindByCurrentStatus(RepoStatus);
var numCollateral = accountsInRepo.Aggregate(0, (total, curAccount) =>
total += curAccount.Collaterals.Count());
return new FactoidResponse
{
CumulativeValue = numCollateral.ToString(),
Name = "Vehicles",
Description = "Current Inventory (REPO)",
FontAwesomeIcon = "fa-car",
Records = accountsInRepo
};
}
public async Task<FactoidResponse> GetAllAccountsO4R()
{
var filteredRepoAccounts = await _reportingRepository.GetAllAccountsInO4R();
return new FactoidResponse
{
CumulativeValue = filteredRepoAccounts.Count().ToString(),
Name = "Accounts",
Description = "Total Accounts in O4R",
FontAwesomeIcon = "fa-car",
Records = filteredRepoAccounts
};
}
public async Task<FactoidResponse> GetPercentFloorPrice(DateTime lowDateTime,
DateTime highDateTime)
{
var soldImsAccounts = await _imsAutoFileDataRepository.GetImsSoldRecordsForPeriod(lowDateTime, highDateTime);
decimal? percentFloor = 0m;
if (soldImsAccounts.Count() != 0)
{
percentFloor = soldImsAccounts.Select(x => x.GrossPercentFloor).Sum() / soldImsAccounts.Count;
}
var records = await _repoAccountRepository.FindByRepoIdAsync(soldImsAccounts.Select(x => x.RepoId.ToString()).ToList());
return new FactoidResponse
{
CumulativeValue = percentFloor?.ToString("#.#") + "%",
Name = "Percentage of Floor",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-money",
Records = records
};
}
public async Task<FactoidResponse> GetRecentlyRedeemed(DateTime lowDate, DateTime highDate)
{
var filteredRepoAccounts = await _repoAccountRepository.FindByStatusChangeInPeriod(RedeemedStatus, lowDate, highDate);
return new FactoidResponse
{
CumulativeValue = filteredRepoAccounts.Count().ToString(),
Name = "Recently Redeemed",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-calendar",
Records = filteredRepoAccounts
};
}
public async Task<FactoidResponse> GetAvgDaysO4RToREPO(DateTime lowDate, DateTime highDate)
{
var recentAccountsInREPO = await _repoAccountRepository.FindByStatusChangeInPeriod(RepoStatus, lowDate, highDate);
var avgDaysO4RtoRepo = GetAverageDaysBetweenStatuses(recentAccountsInREPO, RepoStatus, O4RStatus);
return new FactoidResponse
{
CumulativeValue = avgDaysO4RtoRepo != null ? avgDaysO4RtoRepo.ToString() : "N/A",
Name = "Avg. Days O4R to Repo",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-calendar",
Records = recentAccountsInREPO
};
}
public async Task<FactoidResponse> GetGetAvgDaysDelqToO4R(DateTime lowDate, DateTime highDate)
{
IEnumerable<RepoAccount> recentAccountsInO4R = await _repoAccountRepository.FindByStatusChangeInPeriod(O4RStatus, lowDate, highDate);
var accountsWithoutDateDelinquent = recentAccountsInO4R.Where(account => account.DaysDelinquent == null);
if (accountsWithoutDateDelinquent.Count() > 0)
{
Log.Error($"Accounts found without DaysDelinquent when added: {accountsWithoutDateDelinquent} ", accountsWithoutDateDelinquent);
recentAccountsInO4R = recentAccountsInO4R.Except(accountsWithoutDateDelinquent);
}
var totalAccounts = recentAccountsInO4R.Count();
var avgDaysDelqToO4R = 0;
if (totalAccounts > 0)
{
var delqToO4RSum = recentAccountsInO4R.Aggregate(0, (sum, repoAccount) => sum += repoAccount.DaysDelinquent ?? 0);
avgDaysDelqToO4R = delqToO4RSum / totalAccounts;
}
return new FactoidResponse()
{
CumulativeValue = avgDaysDelqToO4R.ToString(),
Name = "Avg. Days Dlqt To O4R",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-calendar",
Records = recentAccountsInO4R.ToList()
};
}
public async Task<FactoidResponse> GetGetAvgDaysRepoToSold(DateTime lowDate, DateTime highDate)
{
var recentAccountsSold = await _repoAccountRepository.FindByStatusChangeInPeriod("SOLD", lowDate, highDate);
var avgDaysRepoToSold = GetAverageDaysBetweenStatuses(recentAccountsSold, "SOLD", RepoStatus);
return new FactoidResponse
{
CumulativeValue = avgDaysRepoToSold != null ? avgDaysRepoToSold.ToString() : "N/A",
Name = "Avg. Days Repo to Sold",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-calendar",
Records = recentAccountsSold
};
}
public async Task<FactoidResponse> GetCurrentlyOnHold()
{
var accountsOnHold = await _repoAccountRepository.GetCurrentAccountsOnHoldAtAuction();
return new FactoidResponse
{
CumulativeValue = accountsOnHold.Count().ToString(),
Name = "On Hold at Auction",
Description = "Accounts",
FontAwesomeIcon = "fa-car",
Records = accountsOnHold
};
}
public async Task<FactoidResponse> GetRecentlyRepoed(DateTime lowDate, DateTime highDate)
{
var accountsRecentlyRepoed = await _repoAccountRepository.FindByStatusChangeInPeriod(RepoStatus, lowDate, highDate);
return new FactoidResponse
{
CumulativeValue = accountsRecentlyRepoed.Count().ToString(),
Name = "Recently Repoed",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-car",
Records = accountsRecentlyRepoed
};
}
public async Task<FactoidResponse> GetRecentlySold(DateTime lowDate, DateTime highDate)
{
var accountsRecentlySold = await _repoAccountRepository.FindByStatusChangeInPeriod("SOLD", lowDate, highDate);
return new FactoidResponse
{
CumulativeValue = accountsRecentlySold.Count().ToString(),
Name = "Recently Sold",
Description = "Last 30 Days",
FontAwesomeIcon = "fa-car",
Records = accountsRecentlySold
};
}
// Get the average day difference between two status changes.
// If the accounts given don't have valid to and from status, those accounts are ignored from the calulcation.
// However, if calling this array, the accounts SHOULD have valid status changes.
private int? GetAverageDaysBetweenStatuses(IEnumerable<RepoAccount> repoAccount, string statusTo, string statusFrom)
{
IEnumerable<int?> daysBetweenStatuses = repoAccount.Select(
(account) => GetDaysBetweenStatuses(account, statusTo, statusFrom));
var validRepos = daysBetweenStatuses.Aggregate(0, (sum, days) => sum += days != null ? 1 : 0);
var sumOfDays = daysBetweenStatuses.Aggregate(0, (sum, days) => sum += days != null ? days.Value : 0);
if (validRepos == 0)
{
return null;
}
return sumOfDays / validRepos;
}
// If either status cannot be found, return null, indicating an that days could not be calculated.
// If both the from status and to status are found, floor their difference of dates.
private int? GetDaysBetweenStatuses(RepoAccount repoAccount, string newStatus, string oldStatus)
{
var fromStatusDate = GetLatestStatusChange(repoAccount, oldStatus)?.ChangedDate;
var toStatusDate = GetLatestStatusChange(repoAccount, newStatus)?.ChangedDate;
if (toStatusDate != null && fromStatusDate != null)
{
return (int) Math.Floor(toStatusDate.Value.Subtract(fromStatusDate.Value).TotalDays);
}
return null;
}
private RepoStatusChange GetLatestStatusChange(RepoAccount repoAccount, string status)
{
return (
from statusChange in repoAccount.RepoStatusChanges
where string.Equals(statusChange.StatusTo, status, StringComparison.CurrentCultureIgnoreCase)
select statusChange).LastOrDefault();
}
}
}