461 lines
20 KiB
C#
461 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.SqlClient;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
using LCU.API.Collections.RepoApi.Exceptions;
|
|
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.Results;
|
|
using LCU.Shared.Collections.RepoApiLib.Repositories;
|
|
using LCU.Shared.Collections.RepoApiLib.WebServices.AutoIMS;
|
|
using Serilog;
|
|
using AuctionAssignment = LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment;
|
|
|
|
namespace LCU.API.Collections.RepoApi.Services
|
|
{
|
|
public class AuctionAssignmentService : IAuctionAssignmentService
|
|
{
|
|
private readonly IAuctionAssignmentRepository _auctionAssignmentRepository;
|
|
private readonly IUnitOfWork _unitOfWork;
|
|
private readonly IEmailer _emailer;
|
|
private readonly IAutoImsWebService _autoImsWebService;
|
|
private readonly IRepoAccountService _repoAccountService;
|
|
|
|
public AuctionAssignmentService(
|
|
IAutoImsWebService autoImsWebService,
|
|
IRepoAccountService repoAccountService,
|
|
IAuctionAssignmentRepository auctionAssignmentRepository,
|
|
IUnitOfWork unitOfWork,
|
|
IEmailer emailer)
|
|
{
|
|
_autoImsWebService = autoImsWebService;
|
|
_repoAccountService = repoAccountService;
|
|
_auctionAssignmentRepository = auctionAssignmentRepository;
|
|
_unitOfWork = unitOfWork;
|
|
_emailer = emailer;
|
|
}
|
|
|
|
public async Task<PagedResult<AuctionAssignment>> ListByFilterAsync(AuctionAssignmentFilter filter)
|
|
{
|
|
try
|
|
{
|
|
if (filter.ReadyForFloorPrice)
|
|
{
|
|
return await _auctionAssignmentRepository.ListReadyForFloorPriceAsync();
|
|
}
|
|
return await _auctionAssignmentRepository.ListAllAsync(filter.Page, filter.Limit);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failure retrieving AuctionAssignments");
|
|
throw new ServerErrorException();
|
|
}
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<AuctionAssignment>> GetListByIdsAsync(IReadOnlyCollection<int> idSet)
|
|
{
|
|
try
|
|
{
|
|
return await _auctionAssignmentRepository.FindListByIdsAsync(idSet);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure retrieving AuctionAssignments for ids: {@idSet}");
|
|
throw new ServerErrorException();
|
|
}
|
|
}
|
|
|
|
public async Task<AuctionAssignment> GetByIdAsync(int id)
|
|
{
|
|
AuctionAssignment auctionAssignment;
|
|
try
|
|
{
|
|
auctionAssignment = await _auctionAssignmentRepository.FindByIdAsync(id);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure retrieving AuctionAssignment with id: {id}");
|
|
throw new ServerErrorException();
|
|
}
|
|
|
|
if (auctionAssignment == null)
|
|
{
|
|
throw new NotFoundException($" AuctionAssignment with {id} not found.");
|
|
}
|
|
|
|
return auctionAssignment;
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<AuctionAssignment>> GetByRepoIdAsync(int repoId)
|
|
{
|
|
// Ensure the repo exists. If it doesn't, a NFE should be thrown here
|
|
await _repoAccountService.Get(repoId);
|
|
try
|
|
{
|
|
return await _auctionAssignmentRepository.FindByRepoIdAsync(repoId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure retrieving AuctionAssignments by RepoId with RepoID: {repoId}");
|
|
throw new ServerErrorException();
|
|
}
|
|
}
|
|
|
|
public async Task<AuctionAssignmentPostRequest> SaveAsync(AuctionAssignmentPostRequest auctionAssignmentPostRequest)
|
|
{
|
|
Collateral primaryCollateral = auctionAssignmentPostRequest.Collaterals
|
|
.First(collateral => collateral.CollateralId == auctionAssignmentPostRequest.PrimaryCollateralId);
|
|
|
|
string auctionCustomerNo;
|
|
try
|
|
{
|
|
auctionCustomerNo = await GetAuctionCustomerNoAsync(auctionAssignmentPostRequest.AuctionAssignment.RepoId.Value, primaryCollateral.RefCollateralId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure retrieving Auction Customer Number with auctionAssignmentPostRequest {auctionAssignmentPostRequest}", auctionAssignmentPostRequest);
|
|
throw new ServerErrorException();
|
|
}
|
|
|
|
// If active auction already exists, do not create
|
|
var conflictingAuctionAssignments = await _findActiveAuctionsWithMatchingCollateral(primaryCollateral.RepoId, auctionAssignmentPostRequest.Collaterals);
|
|
if (conflictingAuctionAssignments!= null && conflictingAuctionAssignments.Any())
|
|
{
|
|
throw new DuplicateResourceException("AuctionAssignment");
|
|
}
|
|
|
|
try
|
|
{
|
|
var auctionAssignmentsToCollaterals = CreateJoiningAuctionAssignmentToCollateralsList(
|
|
auctionAssignmentPostRequest.AuctionAssignment, auctionAssignmentPostRequest.Collaterals);
|
|
|
|
auctionAssignmentPostRequest.AuctionAssignment.AuctionCustomerNo = auctionCustomerNo;
|
|
auctionAssignmentPostRequest.AuctionAssignment.DateSubmitted = DateTime.Now;
|
|
auctionAssignmentPostRequest.AuctionAssignment.CollateralVIN = primaryCollateral.Vin;
|
|
auctionAssignmentPostRequest.AuctionAssignment.AuctionAssignmentToCollaterals = auctionAssignmentsToCollaterals;
|
|
|
|
auctionAssignmentPostRequest.AuctionAssignment = await _auctionAssignmentRepository.AddAsync(auctionAssignmentPostRequest.AuctionAssignment);
|
|
|
|
await _unitOfWork.CompleteAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure creating AuctionAssignment object: {auctionAssignmentPostRequest.AuctionAssignment}.", auctionAssignmentPostRequest.AuctionAssignment);
|
|
throw new ServerErrorException();
|
|
}
|
|
|
|
try
|
|
{
|
|
await CreateAutoIMSAuction(primaryCollateral, auctionAssignmentPostRequest.VehicleLocation, auctionAssignmentPostRequest.AuctionAssignment);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure creating AutoIMS Auction with Primary Collateral: {primaryCollateral} Removing AuctionAssignment...", primaryCollateral);
|
|
|
|
try { _auctionAssignmentRepository.Remove(auctionAssignmentPostRequest.AuctionAssignment); }
|
|
catch (Exception ex2) { Log.Error(ex2, $"Failure removing AuctionAssignment : {auctionAssignmentPostRequest.AuctionAssignment} after failed AutoIMS creation.", auctionAssignmentPostRequest.AuctionAssignment); }
|
|
|
|
throw new WebException("Failed to add auction assignment to AutoIMS.");
|
|
}
|
|
|
|
return auctionAssignmentPostRequest;
|
|
}
|
|
|
|
public async Task<AuctionAssignment> UpdateAsync(AuctionAssignment auctionAssignment)
|
|
{
|
|
var existingAuctionAssignment = await _auctionAssignmentRepository.FindByIdAsync(auctionAssignment.ID);
|
|
|
|
if (existingAuctionAssignment == null)
|
|
{
|
|
throw new NotFoundException($" Auction Assignment with id {auctionAssignment.ID} not found.");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (existingAuctionAssignment.FloorPrice != auctionAssignment.FloorPrice)
|
|
{
|
|
await UpdateAuctionFloor(auctionAssignment, existingAuctionAssignment);
|
|
}
|
|
|
|
if (existingAuctionAssignment.SentTitleDocsDate != auctionAssignment.SentTitleDocsDate)
|
|
{
|
|
await UpdateTitleSentDate(auctionAssignment, existingAuctionAssignment);
|
|
}
|
|
|
|
if (existingAuctionAssignment.RedeemedDate != auctionAssignment.RedeemedDate)
|
|
{
|
|
await UpdateRedeemedDate(auctionAssignment, existingAuctionAssignment);
|
|
}
|
|
|
|
if (existingAuctionAssignment.HoldStatus != auctionAssignment.HoldStatus || existingAuctionAssignment.HoldUntil != auctionAssignment.HoldUntil)
|
|
{
|
|
await UpdateHoldData(auctionAssignment, existingAuctionAssignment);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failed to fully update auction assignment with id {auctionAssignment.ID} in AutoIMS");
|
|
throw new WebException("Failed to fully update auction assignment in AutoIMS.");
|
|
}
|
|
|
|
return auctionAssignment;
|
|
}
|
|
|
|
public async Task<string> GetAuctionCustomerNoAsync(int repoId, string refCollateralId)
|
|
{
|
|
RepoAccount repoAccount = await _repoAccountService.Get(repoId);
|
|
Borrower primaryBorrower = repoAccount.Borrowers.Where((b) => b.OwnershipType == 6).FirstOrDefault();
|
|
|
|
if (primaryBorrower == null)
|
|
{
|
|
throw new NotFoundException($"Primary borrower for Repo Account {repoAccount.AccountNo} not found.");
|
|
}
|
|
|
|
return primaryBorrower.IndividualId + '-' + refCollateralId;
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to create an Auction through AutoIMS.
|
|
*
|
|
* It does not catch and will throw if anything goes wrong.
|
|
* Assume lack of exception equals success.
|
|
*/
|
|
public async Task<WebServiceResponse> CreateAutoIMSAuction(
|
|
Collateral collateral,
|
|
VehicleLocation vehicleLocation,
|
|
AuctionAssignment auctionAssignment)
|
|
{
|
|
|
|
Vehicle vehicle = new Vehicle()
|
|
{
|
|
ClientAccountNumber = auctionAssignment.AuctionCustomerNo,
|
|
VIN = collateral.Vin,
|
|
Make = collateral.Make,
|
|
Model = collateral.Model,
|
|
ModelYear = collateral.Year.GetValueOrDefault(),
|
|
BodyStyle = collateral.BodyStyle,
|
|
Classification = collateral.VehicleType.GetValueOrDefault(),
|
|
ClientField1 = auctionAssignment.ID + "",
|
|
AuctionAssignment = new Shared.Collections.RepoApiLib.WebServices.AutoIMS.AuctionAssignment()
|
|
{
|
|
AuctionID = auctionAssignment.AuctionId,
|
|
PickupDate = auctionAssignment.AvailablePickupDate.GetValueOrDefault().ToString("yyyy-MM-dd"),
|
|
LegalSaleDate = auctionAssignment.LegalSaleDate.GetValueOrDefault().ToString("yyyy-MM-dd"),
|
|
PickupOrDropOffNote = auctionAssignment.AuctionNotes,
|
|
CleaningLevelRequested = auctionAssignment.CleaningLevel,
|
|
},
|
|
VehicleLocation = vehicleLocation,
|
|
};
|
|
|
|
return await _autoImsWebService.PostVehicleAsync<Vehicle>(vehicle);
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to remove an Auction through AutoIMS.
|
|
*
|
|
* It does not catch and will throw if anything goes wrong.
|
|
* Assume lack of exception equals success.
|
|
*/
|
|
public async Task<WebServiceResponse> RemoveAuctionFromAutoIMS(AuctionAssignment auctionAssignment)
|
|
{
|
|
return await _autoImsWebService.RemoveFromAuction(auctionAssignment.AuctionCustomerNo, auctionAssignment.CollateralVIN);
|
|
}
|
|
|
|
public IEnumerable<AuctionAssignmentToCollateral> CreateJoiningAuctionAssignmentToCollateralsList(AuctionAssignment auctionAssignment, IEnumerable<Collateral> collaterals)
|
|
{
|
|
return (from collateral in collaterals
|
|
select new AuctionAssignmentToCollateral
|
|
{
|
|
AuctionAssignment = auctionAssignment,
|
|
Collateral = collateral
|
|
}).ToList();
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to call AutoIMS with an auction floor, and will not update the
|
|
* floor related values unless it succeeds
|
|
*/
|
|
public async Task UpdateAuctionFloor(AuctionAssignment auctionAssignmentInput,
|
|
AuctionAssignment existingAssignment)
|
|
{
|
|
FloorPriceVehicle vehicle = new FloorPriceVehicle();
|
|
vehicle.ClientAccountNumber = auctionAssignmentInput.AuctionCustomerNo;
|
|
vehicle.VIN = auctionAssignmentInput.CollateralVIN;
|
|
vehicle.AuctionAssignment.FloorPrice = auctionAssignmentInput.FloorPrice ?? 0;
|
|
|
|
WebServiceResponse response = await _autoImsWebService.PostVehicleAsync<FloorPriceVehicle>(vehicle);
|
|
|
|
existingAssignment.FloorPriceResponseCode = response?.Status.Code;
|
|
existingAssignment.FloorPriceResponseDescription = response?.Status.Description;
|
|
|
|
if (response?.Status.ValidationError?.Any() ?? false)
|
|
{
|
|
foreach (var item in response.Status.ValidationError)
|
|
{
|
|
existingAssignment.FloorPriceSubmissionErrors +=
|
|
"Code: " + item.Code + " Message: " + item.Message + "|";
|
|
}
|
|
|
|
}
|
|
else if (response?.Status.Code == 1)
|
|
{
|
|
// e-mail management on successfully accepted floor price change
|
|
var isManualFloorSet = existingAssignment.FloorPriceValuesUsed == auctionAssignmentInput.FloorPriceValuesUsed;
|
|
var repoAccount = await _repoAccountService.FindByAuctionAssignmentId(existingAssignment.ID);
|
|
await _emailer.SendFloorPriceAcceptedEmail(isManualFloorSet, repoAccount?.AccountNo, existingAssignment.FloorPrice, auctionAssignmentInput.FloorPrice);
|
|
|
|
existingAssignment.FloorPrice = auctionAssignmentInput.FloorPrice;
|
|
existingAssignment.FloorPriceValuesUsed = auctionAssignmentInput.FloorPriceValuesUsed;
|
|
}
|
|
|
|
await _completeAuctionAssignmentUpdateTransaction(existingAssignment);
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to call AutoIMS and set the title sent date, and will not update the
|
|
* title sent related values unless it succeeds
|
|
*/
|
|
public async Task UpdateTitleSentDate(AuctionAssignment auctionAssignmentInput,
|
|
AuctionAssignment existingAssignment)
|
|
{
|
|
WebServiceResponse response = await _autoImsWebService.TitleSent(auctionAssignmentInput.CollateralVIN);
|
|
|
|
existingAssignment.TitleSentResponseCode = response?.Status.Code;
|
|
existingAssignment.TitleSentResponseDescription = response?.Status.Description;
|
|
|
|
if (response?.Status.ValidationError?.Any() ?? false)
|
|
{
|
|
foreach (var item in response.Status.ValidationError)
|
|
{
|
|
existingAssignment.TitleSentSubmissionErrors +=
|
|
"Code: " + item.Code + " Message: " + item.Message + "|";
|
|
}
|
|
|
|
}
|
|
else if (response?.Status.Code == 1)
|
|
{
|
|
existingAssignment.SentTitleDocsDate = auctionAssignmentInput.SentTitleDocsDate;
|
|
}
|
|
|
|
await _completeAuctionAssignmentUpdateTransaction(existingAssignment);
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to call AutoIMS and set the redeemed date, and will not update the
|
|
* redeemed date unless it succeeds
|
|
*/
|
|
public async Task UpdateRedeemedDate(AuctionAssignment auctionAssignmentInput,
|
|
AuctionAssignment existingAssignment)
|
|
{
|
|
WebServiceResponse response = await _autoImsWebService.Redeem(auctionAssignmentInput.CollateralVIN);
|
|
|
|
if (response?.Status.ValidationError?.Any() ?? false)
|
|
{
|
|
foreach (var item in response.Status.ValidationError)
|
|
{
|
|
// There are no database columns for redemption submission errors so log the error
|
|
Log.Error($"Redemption submission error. Code: {item.Code} Message: {item.Message} | { auctionAssignmentInput}");
|
|
}
|
|
|
|
}
|
|
else if (response?.Status.Code == 1)
|
|
{
|
|
existingAssignment.RedeemedDate = auctionAssignmentInput.RedeemedDate;
|
|
}
|
|
|
|
await _completeAuctionAssignmentUpdateTransaction(existingAssignment);
|
|
}
|
|
|
|
/**
|
|
* This function will attempt to call AutoIMS to place a hold, and will not update the
|
|
* hold related values unless it succeeds
|
|
*/
|
|
public async Task UpdateHoldData(AuctionAssignment auctionAssignmentInput,
|
|
AuctionAssignment existingAssignment)
|
|
{
|
|
var holdStatus = auctionAssignmentInput.HoldStatus ?? 0;
|
|
var holdUntil = auctionAssignmentInput.HoldUntil ?? DateTime.MinValue;
|
|
|
|
WebServiceResponse response = await _autoImsWebService.PlaceHold(auctionAssignmentInput.CollateralVIN, holdStatus, holdUntil);
|
|
|
|
if (response?.Status.ValidationError?.Any() ?? false)
|
|
{
|
|
foreach (var item in response.Status.ValidationError)
|
|
{
|
|
existingAssignment.SubmissionErrors +=
|
|
"Code: " + item.Code + " Message: " + item.Message + "|";
|
|
}
|
|
|
|
}
|
|
else if (response?.Status.Code == 1)
|
|
{
|
|
existingAssignment.HoldStatus = auctionAssignmentInput.HoldStatus;
|
|
existingAssignment.HoldUntil = auctionAssignmentInput.HoldUntil;
|
|
}
|
|
|
|
await _completeAuctionAssignmentUpdateTransaction(existingAssignment);
|
|
}
|
|
|
|
public async Task<AuctionAssignment> RemoveAuction(int id)
|
|
{
|
|
var auctionAssignment = await _auctionAssignmentRepository.FindByIdAsync(id);
|
|
|
|
if (auctionAssignment == null)
|
|
{
|
|
throw new NotFoundException($" Auction Assignment with id {id} not found.");
|
|
}
|
|
|
|
try
|
|
{
|
|
await RemoveAuctionFromAutoIMS(auctionAssignment);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failed to remove auction assignment from AutoIMS {auctionAssignment.ID}");
|
|
throw new WebException("Failed to remove auction assignment from AutoIMS.");
|
|
}
|
|
|
|
auctionAssignment.IsActive = false;
|
|
await _completeAuctionAssignmentUpdateTransaction(auctionAssignment);
|
|
|
|
return auctionAssignment;
|
|
}
|
|
|
|
private async Task _completeAuctionAssignmentUpdateTransaction(AuctionAssignment auctionAssignment)
|
|
{
|
|
try
|
|
{
|
|
_auctionAssignmentRepository.Update(auctionAssignment);
|
|
await _unitOfWork.CompleteAsync();
|
|
}
|
|
catch (SqlException ex)
|
|
{
|
|
Log.Error(ex, $"SQL Failure updating Auction Assignment: {auctionAssignment}", auctionAssignment);
|
|
throw new ServerErrorException();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure updating Auction Assignment: {auctionAssignment}", auctionAssignment);
|
|
throw new ServerErrorException();
|
|
}
|
|
}
|
|
|
|
private async Task<IEnumerable<AuctionAssignment>> _findActiveAuctionsWithMatchingCollateral(int repoId, IEnumerable<Collateral> collaterals)
|
|
{
|
|
var assignments = await _auctionAssignmentRepository.FindByRepoIdAsync(repoId);
|
|
if (assignments != null && assignments.Any())
|
|
{
|
|
return assignments.Where(auctionAssignment => auctionAssignment.IsActive == true &&
|
|
auctionAssignment.AuctionAssignmentToCollaterals.Any(aa2c => collaterals
|
|
.Select(collateral => collateral.CollateralId)
|
|
.Contains(aa2c.CollateralId)));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
} |