Adding
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Rider
|
||||
.idea
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.645
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Collections.RepoApi", "LCU.API.Collections.RepoApi\LCU.API.Collections.RepoApi.csproj", "{7D12B7C0-7967-41CA-A442-A3F78182F0B5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Collections.RepoApiTests", "LCU.API.Collections.RepoApiTests\LCU.API.Collections.RepoApiTests.csproj", "{EA9E0245-8AA9-4E33-8507-E7C41EFC5DA5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Shared.Collections.RepoApiLib", "LCU.Shared.Collections.RepoApiLib\LCU.Shared.Collections.RepoApiLib.csproj", "{1E93535C-B421-4D9B-97C6-6F4FD0308B3C}"
|
||||
EndProject
|
||||
Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "LCU.API.Collections.RepoApiSql", "LCU.API.Collections.RepoApiSql\LCU.API.Collections.RepoApiSql.sqlproj", "{33A79137-9040-4749-B89D-1055657C3745}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7D12B7C0-7967-41CA-A442-A3F78182F0B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7D12B7C0-7967-41CA-A442-A3F78182F0B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7D12B7C0-7967-41CA-A442-A3F78182F0B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7D12B7C0-7967-41CA-A442-A3F78182F0B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA9E0245-8AA9-4E33-8507-E7C41EFC5DA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA9E0245-8AA9-4E33-8507-E7C41EFC5DA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA9E0245-8AA9-4E33-8507-E7C41EFC5DA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA9E0245-8AA9-4E33-8507-E7C41EFC5DA5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1E93535C-B421-4D9B-97C6-6F4FD0308B3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E93535C-B421-4D9B-97C6-6F4FD0308B3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E93535C-B421-4D9B-97C6-6F4FD0308B3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E93535C-B421-4D9B-97C6-6F4FD0308B3C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{33A79137-9040-4749-B89D-1055657C3745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{33A79137-9040-4749-B89D-1055657C3745}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {458D6DB8-766A-4013-8923-6571AB24E3EF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace LCU.API.Collections.RepoApi.Configuration
|
||||
{
|
||||
/**
|
||||
* These feature flags should match what is in the appsettings under "FeatureFlags" to prevent Magic Strings.
|
||||
*/
|
||||
public static class FeatureFlags
|
||||
{
|
||||
public const string EnableControllers = "EnableControllers";
|
||||
public const string RunRepoHostedService = "RunRepoHostedService";
|
||||
public const string RunAutoImsHostedService = "RunAutoImsHostedService";
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.AutoIms;
|
||||
using System.Collections.Generic;
|
||||
using LCU.Shared.Realtime.QueueLib.Models;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Configuration
|
||||
{
|
||||
public class RepoApiConfiguration : IHostedTaskSchedulerConfig
|
||||
{
|
||||
public string ExchangeName { get; set; }
|
||||
public IReadOnlyCollection<ScheduledTaskConfig> TaskConfigs { get; set; }
|
||||
public string OrganizationCode { get; set; }
|
||||
public int SettingsCacheExpirationSeconds { get; set; }
|
||||
public List<string> ProgrammingRecipients { get; set; }
|
||||
public List<string> RepoManagerAdminRecipients { get; set; }
|
||||
public List<string> FloorPriceAcceptedRecipients { get; set; }
|
||||
public List<string> CollectionsClerkRecipients { get; set; }
|
||||
public List<string> CollectionsRecipients { get; set; }
|
||||
public List<string> CreditDebitsRecipients { get; set; }
|
||||
public List<string> TransactionTypeCodes { get; set; }
|
||||
public string TransactionTypeCodeQueryPrefix { get; set; }
|
||||
public string TransactionHistoryParameterDateFormat { get; set; }
|
||||
public int TransactionHistoryStartDateOffset { get; set; }
|
||||
public string NotificationsEmailUrl { get; set; }
|
||||
public string RepoManagerAccountUrl { get; set; }
|
||||
|
||||
// URLs for data from Collections (Intellect/CORS/Temenos)
|
||||
public string CollectionsLetterUrl { get; set; }
|
||||
public string CollectionAccountUrl { get; set; }
|
||||
|
||||
// URLs for data from Core.
|
||||
public string CoreLoanUrl { get; set; }
|
||||
public string CoreCollateralUrl { get; set; }
|
||||
public string CoreMemberUrl { get; set; }
|
||||
public string CoreLoanTransactions { get; set; }
|
||||
public string CoreBranchSetUrl { get; set; }
|
||||
|
||||
// Auto IMS Import
|
||||
public FtpConfiguration FtpConfiguration { get; set; }
|
||||
public string RemoteFilePath { get; set; }
|
||||
public string LocalDirectoryPath { get; set; }
|
||||
public string ConditionReportUrl { get; set; }
|
||||
public bool MoveProcessedFiles { get; set; }
|
||||
public bool DownloadNewFiles { get; set; }
|
||||
public bool CheckForLocalFiles { get; set; }
|
||||
public string CollectionsConfigUrl { get; set; }
|
||||
|
||||
public IReadOnlyCollection<ScheduledTaskConfig> GetTaskConfigurations()
|
||||
{
|
||||
return TaskConfigs;
|
||||
}
|
||||
|
||||
public string GetExchangeName()
|
||||
{
|
||||
return ExchangeName;
|
||||
}
|
||||
|
||||
public IDictionary<string, QueueConsumerConfig> QueueConsumerConfigs { get; set; }
|
||||
}
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Models.Filters;
|
||||
using LCU.API.Collections.RepoApi.Models.Requests;
|
||||
using LCU.API.Collections.RepoApi.Models.Responses;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Exceptions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class AuctionAssignmentController : RepoControllerBase
|
||||
{
|
||||
private readonly IAuctionAssignmentService _auctionAssignmentService;
|
||||
private readonly IFloorPricingService _floorPricingService;
|
||||
|
||||
public AuctionAssignmentController(
|
||||
IAuctionAssignmentService auctionAssignmentService,
|
||||
IFloorPricingService floorPricingService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_auctionAssignmentService = auctionAssignmentService;
|
||||
_floorPricingService = floorPricingService;
|
||||
}
|
||||
|
||||
// GET: api/AuctionAssignment
|
||||
[HttpGet(Name = "GetAllAuctionAssignments")]
|
||||
[ProducesResponseType(typeof(PagedResponse<AuctionAssignment>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get([FromQuery] AuctionAssignmentFilter filter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var searchResult = await _auctionAssignmentService.ListByFilterAsync(filter);
|
||||
var result = new PagedResponse<AuctionAssignment>(searchResult, filter, this.Url, Request);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/AuctionAssignment/5
|
||||
[HttpGet("{id}", Name = "GetAuctionAssignmentById")]
|
||||
[ProducesResponseType(typeof(AuctionAssignment), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _auctionAssignmentService.GetByIdAsync(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// Get: api/AuctionAssignment/repoId/7
|
||||
[HttpGet("repoId/{repoId}", Name = "GetAuctionAssignmentsByRepoId")]
|
||||
[ProducesResponseType(typeof(IEnumerable<AuctionAssignment>), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetByRepoId(int repoId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (repoId <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _auctionAssignmentService.GetByRepoIdAsync(repoId);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/AuctionAssignment/5/FloorPrice
|
||||
[HttpGet("{id}/FloorPrice", Name = "GetAuctionAssignmentFloorPriceById")]
|
||||
[ProducesResponseType(typeof(FloorPriceCalculationResultSet), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public IActionResult GetAuctionAssignmentFloorPriceById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = _floorPricingService.DetermineFloorPrice(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
Log.Error(nfe, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/AuctionAssignment
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(AuctionAssignmentPostRequest), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] AuctionAssignmentPostRequest auctionAssignmentPostRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _auctionAssignmentService.SaveAsync(auctionAssignmentPostRequest);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
Log.Error(we, $"Failure in {this.GetType().Name} at [AuctionAssignmentPostRequest]");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, we.Message);
|
||||
}
|
||||
catch (DuplicateResourceException)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status409Conflict, "An active auction assignment already exists. You must remove the auction before creating a new one.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to add auction to AutoIMS.");
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/AuctionAssignment/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(AuctionAssignment), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] AuctionAssignment auctionAssignment)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (id <= 0 || id != auctionAssignment.ID)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _auctionAssignmentService.UpdateAsync(auctionAssignment);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
Log.Error(nfe, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: api/ApiWithActions/5
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(typeof(AuctionAssignment), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var deletedRepoCompany = await _auctionAssignmentService.RemoveAuction(id);
|
||||
return Ok(deletedRepoCompany);
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, we.Message);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to remove auction from AutoIMS.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class AuctionHouseController : RepoControllerBase
|
||||
{
|
||||
private readonly IAuctionHouseService _auctionHouseService;
|
||||
|
||||
public AuctionHouseController(
|
||||
IAuctionHouseService auctionHouseService,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
: base( httpContextAccessor)
|
||||
{
|
||||
_auctionHouseService = auctionHouseService;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetAllAuctionHouses")]
|
||||
[ProducesResponseType(typeof(IEnumerable<AuctionHouse>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var auctions = await _auctionHouseService.ListAllAsync();
|
||||
return Ok(auctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{id}", Name = "GetAuctionHouseById")]
|
||||
[ProducesResponseType(typeof(AuctionHouse), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var auction = await _auctionHouseService.GetAsync(id);
|
||||
return Ok(auction);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(AuctionHouse), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
||||
public async Task<IActionResult> Post([FromBody] AuctionHouse auction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var newAuction = await _auctionHouseService.SaveAsync(auction);
|
||||
return Ok(newAuction);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(AuctionHouse), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
||||
public async Task<IActionResult> Put(string id, [FromBody] AuctionHouse auction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (id != auction.AuctionID)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var updatedAuction = await _auctionHouseService.UpdateAsync(auction);
|
||||
return Ok(updatedAuction);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(typeof(AuctionHouse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deletedAuction = await _auctionHouseService.DeleteAsync(id);
|
||||
return Ok(deletedAuction);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Models.Filters;
|
||||
using LCU.API.Collections.RepoApi.Models.Responses;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class AuditRecordController : RepoControllerBase
|
||||
{
|
||||
private readonly IAuditRecordService _auditRecordService;
|
||||
|
||||
public AuditRecordController(
|
||||
IAuditRecordService repoAccountService,
|
||||
IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
_auditRecordService = repoAccountService;
|
||||
}
|
||||
|
||||
// GET: api/AuditRecord
|
||||
[HttpGet(Name = "GetAllAuditRecords")]
|
||||
[ProducesResponseType(typeof(PagedResponse<AuditRecord>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get([FromQuery] PageFilter filter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var searchResult = await _auditRecordService.ListAll(filter);
|
||||
var result = new PagedResponse<AuditRecord>(searchResult, filter, Url, Request);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/AuditRecord/5
|
||||
[HttpGet("{id}", Name = "GetAuditRecordById")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _auditRecordService.Get(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/RepoAccount
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(RepoAccount), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] AuditRecord repoAccount)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _auditRecordService.SaveAsync(repoAccount);
|
||||
return Ok( result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class CollateralController : RepoControllerBase
|
||||
{
|
||||
private readonly ICollateralService _collateralService;
|
||||
|
||||
public CollateralController(
|
||||
ICollateralService collateralService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_collateralService = collateralService;
|
||||
}
|
||||
|
||||
// GET: api/Collateral
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<Collateral>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _collateralService.ListAll();
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/Collateral/5
|
||||
[HttpGet("{id}", Name = "GetCollateralById")]
|
||||
[ProducesResponseType(typeof(Collateral), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _collateralService.Get(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Collateral
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(Collateral), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] Collateral collateral)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _collateralService.SaveAsync(collateral);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/Collateral/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(Collateral), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] Collateral collateral)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id <= 0 || id != collateral.CollateralId)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _collateralService.UpdateAsync(collateral);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Exceptions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class EventAlertController : RepoControllerBase
|
||||
{
|
||||
private readonly IEventAlertService _eventAlertService;
|
||||
private readonly IRepoAccountService _repoAccountService;
|
||||
|
||||
public EventAlertController(
|
||||
IEventAlertService eventAlertService,
|
||||
IRepoAccountService repoAccountService,
|
||||
IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
_eventAlertService = eventAlertService;
|
||||
_repoAccountService = repoAccountService;
|
||||
}
|
||||
|
||||
// GET: api/EventAlert
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<EventAlert>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get(bool unreadOnly = false, bool actionNeeded = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (unreadOnly)
|
||||
{
|
||||
var unread = await _eventAlertService.ListUnread();
|
||||
return Ok(unread);
|
||||
}
|
||||
|
||||
if (actionNeeded)
|
||||
{
|
||||
var needed = await _eventAlertService.ListActionNeeded();
|
||||
return Ok(needed);
|
||||
}
|
||||
|
||||
var results = await _eventAlertService.ListAll();
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/EventAlert/5
|
||||
[HttpGet("{id}", Name = "GetEventAlertById")]
|
||||
[ProducesResponseType(typeof(EventAlert), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _eventAlertService.Get(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/EventAlert/
|
||||
[HttpGet]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(EventAlert), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetWithinDateRange(DateTime start, DateTime end)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (start == null || end == null || end < start)
|
||||
{
|
||||
return BadRequest("Invalid dates");
|
||||
}
|
||||
|
||||
var result = await _eventAlertService.ListWithinDateRange(start, end);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/EventAlert
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(EventAlert), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] EventAlert eventAlert)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _eventAlertService.SaveAsync(eventAlert);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (DuplicateResourceException)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status409Conflict, "A Duplicate Event Alert already exists.");
|
||||
}
|
||||
catch (UnwantedStatusException)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status409Conflict, $"Status of Repo: Reposession ID: {eventAlert.RepoId} is Cancelled or Redeemed. Event Alert not created.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/EventAlert/ByAuctionAssignmentId/:AuctionAssignmentId
|
||||
[HttpPost("ByAuctionAssignmentId/{auctionAssignmentId}")]
|
||||
[ProducesResponseType(typeof(EventAlert), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> CreateByAuctionAssignmentId(int auctionAssignmentId,
|
||||
[FromBody] EventAlert eventAlert)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var repo = await _repoAccountService.FindByAuctionAssignmentId(auctionAssignmentId);
|
||||
if (repo == null)
|
||||
{
|
||||
return BadRequest("Repo Account Not Found For Auction Assignment");
|
||||
}
|
||||
|
||||
eventAlert.RepoId = repo.RepoId;
|
||||
eventAlert.AccountNo = repo.AccountNo;
|
||||
var result = await _eventAlertService.SaveAsync(eventAlert);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/EventAlert/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(EventAlert), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] EventAlert eventAlert)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id <= 0 || id != eventAlert.Id)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var currentUser = CreateUserFromAuth();
|
||||
|
||||
var result = await _eventAlertService.UpdateAsync(eventAlert, currentUser);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: api/ApiWithActions/5
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(typeof(EventAlert), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _eventAlertService.DeleteAsync(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {this.GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Models.Responses;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class FactoidController : RepoControllerBase
|
||||
{
|
||||
private readonly IFactoidService _factoidService;
|
||||
|
||||
public FactoidController(IHttpContextAccessor httpContextAccessor,
|
||||
IFactoidService factoidService) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_factoidService = factoidService;
|
||||
}
|
||||
|
||||
// GET: api/Factoid
|
||||
[HttpGet(Name = "GetAllFactoids")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FactoidResponse>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAllFactoids()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = new List<FactoidResponse>()
|
||||
{
|
||||
await _factoidService.GetAllAccountsO4R(),
|
||||
await _factoidService.GetAvgDaysO4RToREPO(lowDate, highDate),
|
||||
await _factoidService.GetCurrentInventory(),
|
||||
await _factoidService.GetPercentFloorPrice(lowDate, highDate),
|
||||
await _factoidService.GetCurrentlyOnHold(),
|
||||
await _factoidService.GetRecentlyRedeemed(lowDate, highDate),
|
||||
await _factoidService.GetRecentlyRepoed(lowDate, highDate),
|
||||
await _factoidService.GetGetAvgDaysDelqToO4R(lowDate, highDate),
|
||||
await _factoidService.GetGetAvgDaysRepoToSold(lowDate, highDate),
|
||||
await _factoidService.GetRecentlySold(lowDate, highDate)
|
||||
};
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/CurrentInventory
|
||||
[HttpGet("CurrentInventory", Name = "GetCurrentInventory")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetCurrentInventory()
|
||||
{
|
||||
try
|
||||
{
|
||||
var factoidResponse = await _factoidService.GetCurrentInventory();
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/AccountsO4R
|
||||
[HttpGet("AccountsO4R", Name = "GetAllAccountsInO4R")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAllAccountsO4R()
|
||||
{
|
||||
try
|
||||
{
|
||||
var factoidResponse = await _factoidService.GetAllAccountsO4R();
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/PercentFloor
|
||||
[HttpGet("PercentFloor", Name = "GetPercentFloor")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetPercentFloor()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetPercentFloorPrice(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/RecentlyRedeemed
|
||||
[HttpGet("RecentlyRedeemed", Name = "GetRecentlyRedeemed")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRecentlyRedeemed()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetRecentlyRedeemed(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/AvgDaysO4RToREPO
|
||||
[HttpGet("AvgDaysO4RToREPO", Name = "GetAvgDaysO4RToRepo")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgDaysO4RToRepo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetAvgDaysO4RToREPO(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/AvgDaysDelqToO4R
|
||||
[HttpGet("AvgDaysDelqToO4R", Name = "GetAvgDaysDelqToO4R")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgDaysDelqToO4R()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetGetAvgDaysDelqToO4R(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/AvgDaysRepoToSold
|
||||
[HttpGet("AvgDaysRepoToSold", Name = "GetAvgDaysRepoToSold")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgDaysRepoToSold()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetGetAvgDaysRepoToSold(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/CurrentOnHold
|
||||
[HttpGet("CurrentOnHold", Name = "GetCurrentOnHold")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetCurrentOnHold()
|
||||
{
|
||||
try
|
||||
{
|
||||
var factoidResponse = await _factoidService.GetCurrentlyOnHold();
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/RecentlyRepoed
|
||||
[HttpGet("RecentlyRepoed", Name = "GetRecentlyRepoed")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRecentlyRepoed()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetRecentlyRepoed(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/factoid/RecentlySold
|
||||
[HttpGet("RecentlySold", Name = "GetRecentlySold")]
|
||||
[ProducesResponseType(typeof(FactoidResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRecentlySold()
|
||||
{
|
||||
try
|
||||
{
|
||||
var highDate = DateTime.Today;
|
||||
var lowDate = highDate.Subtract(TimeSpan.FromDays(30));
|
||||
|
||||
var factoidResponse = await _factoidService.GetRecentlySold(lowDate, highDate);
|
||||
return Ok(factoidResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at Factoid: {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class LoggingController : RepoControllerBase
|
||||
{
|
||||
public LoggingController(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use to send something to the logger from the client
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Logs a message from a client.
|
||||
/// Cookies: None required or set.
|
||||
/// Security: Must have a valid JWT token retrieved from Azure AD, with ADMIN role.
|
||||
/// </remarks>
|
||||
// Post: api/Logging/Error
|
||||
[HttpPost("Error")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(400)]
|
||||
[ProducesResponseType(403)]
|
||||
[ProducesResponseType(500)]
|
||||
public IActionResult Post([FromBody] ClientError error)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Error($"Client Exception: <{ error.Name}> : {error.Message} => {error.StackTrace}");
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e,"Failed to log client error. Exception Thrown.");
|
||||
return StatusCode((int) System.Net.HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.HostedServices;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class ProcessingReceiptController : RepoControllerBase
|
||||
{
|
||||
|
||||
private readonly IProcessingReceiptService _receiptService;
|
||||
|
||||
public ProcessingReceiptController(IProcessingReceiptService receiptService,
|
||||
IHttpContextAccessor httpContextAccessor):
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_receiptService = receiptService;
|
||||
}
|
||||
|
||||
//GET: api/ProcessingReceipt/AccountNo/5
|
||||
[HttpGet("AccountNo/{accountNo}")]
|
||||
[ProducesResponseType(typeof(ReadOnlyCollection<ProcessingReceipt>),200)]
|
||||
[ProducesResponseType(typeof(string),400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetByAccountNo(string accountNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace((accountNo)))
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var results = await _receiptService.GetReceiptsByAccountNoAsync(accountNo);
|
||||
if (results.Any())
|
||||
{
|
||||
return Ok(results);
|
||||
}
|
||||
|
||||
return NotFound("No receipts found for for this account");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
using LCU.API.Collections.RepoApi.HostedServices;
|
||||
using LCU.API.Collections.RepoApi.Models.Requests;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Producer;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class QueueController : RepoControllerBase
|
||||
{
|
||||
private readonly IRepoQueueProducerService _repoQueueProducer;
|
||||
|
||||
public QueueController(IRepoQueueProducerService producerService, IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_repoQueueProducer = producerService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public IActionResult PutSingleAccountOnQueue([FromBody] AccountQueueRequest queueRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var currentUser = CreateUserFromAuth();
|
||||
|
||||
var request = new ProcessingItem()
|
||||
{
|
||||
AccountNo = queueRequest.AccountNo,
|
||||
CaseId = queueRequest.CaseId,
|
||||
QueuedBy = currentUser.Email
|
||||
};
|
||||
|
||||
_repoQueueProducer.SendRequestToQueue(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode((int) System.Net.HttpStatusCode.InternalServerError,
|
||||
$"A server error occured, and your request could not be complete");
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class RefundStatusController : RepoControllerBase
|
||||
{
|
||||
private IRefundStatusService _refundStatusService;
|
||||
|
||||
public RefundStatusController(
|
||||
IRefundStatusService refundStatusService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_refundStatusService = refundStatusService;
|
||||
}
|
||||
|
||||
// GET: api/RefundStatus
|
||||
[HttpGet(Name = "GetAllRefundStatus")]
|
||||
[ProducesResponseType(typeof(IEnumerable<RefundStatus>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _refundStatusService.ListAllAsync();
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RefundStatus/5
|
||||
[HttpGet("{repoId}", Name = "GetRefundStatusById")]
|
||||
[ProducesResponseType(typeof(RefundStatus), 200)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int repoId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (repoId <= 0)
|
||||
{
|
||||
return BadRequest("Invalid RepoId");
|
||||
}
|
||||
|
||||
var refundStatus = await _refundStatusService.GetAsync(repoId);
|
||||
return Ok(refundStatus);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(RefundStatus), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] RefundStatus refundStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (refundStatus.RepoId <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var newRefundStatus = await _refundStatusService.SaveAsync(refundStatus);
|
||||
return Ok(newRefundStatus);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{repoId}")]
|
||||
[ProducesResponseType(typeof(RefundStatus), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Put(int repoId, [FromBody] RefundStatus refundStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (repoId <= 0 || repoId != refundStatus.RepoId)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var newRefundStatus = await _refundStatusService.UpdateAsync(refundStatus);
|
||||
return Ok(newRefundStatus);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Models;
|
||||
using LCU.API.Collections.RepoApi.Models.Filters;
|
||||
using LCU.API.Collections.RepoApi.Models.Requests;
|
||||
using LCU.API.Collections.RepoApi.Models.Responses;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Exceptions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class RepoAccountController : RepoControllerBase
|
||||
{
|
||||
private readonly IRepoAccountService _repoAccountService;
|
||||
private readonly IRepoLetterService _repoLetterService;
|
||||
private readonly IRepoStatusChangeService _repoStatusChangeService;
|
||||
|
||||
public RepoAccountController(
|
||||
IRepoAccountService repoAccountService,
|
||||
IRepoLetterService repoLetterService,
|
||||
IRepoStatusChangeService repoStatusChangeService,
|
||||
IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
_repoAccountService = repoAccountService;
|
||||
_repoLetterService = repoLetterService;
|
||||
_repoStatusChangeService = repoStatusChangeService;
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount
|
||||
[HttpGet(Name = "GetAllRepoAccounts")]
|
||||
[ProducesResponseType(typeof(PagedResponse<RepoAccount>), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get([FromQuery] PageFilter filter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var searchResult = await _repoAccountService.ListAll(filter);
|
||||
var result = new PagedResponse<RepoAccount>(searchResult, filter, this.Url, Request);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount/5
|
||||
[HttpGet("{id}", Name = "GetRepoAccountById")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _repoAccountService.Get(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount/5/statusChanges
|
||||
[HttpGet("{id}/statusChanges", Name = "GetRepoAccountStatusChanges")]
|
||||
[ProducesResponseType(typeof(RepoStatusChange), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetStatusChangesByRepoId(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _repoStatusChangeService.ListByAccount(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount/5/repoLetters
|
||||
[HttpGet("{id}/repoLetters", Name = "GetLettersByRepoId")]
|
||||
[ProducesResponseType(typeof(RepoLetter), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetLettersByRepoId(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _repoLetterService.ListByAccountIdAsync(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount/Search/searchPhrase
|
||||
[HttpGet("Search/{searchPhrase}", Name = "SearchRepoAccounts")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> SearchRepoAccounts(string searchPhrase)
|
||||
{
|
||||
var maxResults = 100;
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(searchPhrase))
|
||||
{
|
||||
return BadRequest("Search Phrase Cannot Be Empty");
|
||||
}
|
||||
|
||||
if (searchPhrase.Trim().Length < 3)
|
||||
{
|
||||
return BadRequest("Search Phrase Must Be At Least Three (3) Characters Long");
|
||||
}
|
||||
|
||||
var result = await _repoAccountService.SearchRepoAccounts(searchPhrase, maxResults);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (MaxItemException ex)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Too Many Search Results. (Max: {maxResults}) (Found: {ex.ItemCount})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoAccount/AccountNo/5
|
||||
[HttpGet("AccountNo/{accountNo}", Name = "GetRepoAccountByAccountNo")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetByAccountNo(string accountNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(accountNo))
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _repoAccountService.GetByAccountNo(accountNo);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/RepoAccount
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(RepoAccount), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] RepoAccount repoAccount)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _repoAccountService.SaveAsync(repoAccount);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/RepoAccount/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] RepoAccount repoAccount)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0 || id != repoAccount.RepoId)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
var currentUser = CreateUserFromAuth();
|
||||
var result = await _repoAccountService.UpdateAsync(repoAccount, currentUser);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (MaxItemException mie)
|
||||
{
|
||||
return BadRequest(mie.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/RepoAccount/Insecure/5
|
||||
[HttpPut("Insecure/{id}", Name = "Update Account Insecure Batch")]
|
||||
[ProducesResponseType(typeof(RepoAccount), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutBatch(int id, [FromBody] RepoAccount repoAccount)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0 || id != repoAccount.RepoId)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var currentUser = new AppUser()
|
||||
{
|
||||
FirstName = "System",
|
||||
LastName = "User"
|
||||
};
|
||||
var result = await _repoAccountService.UpdateAsync(repoAccount, currentUser);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (MaxItemException mie)
|
||||
{
|
||||
return BadRequest(mie.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Filter
|
||||
[HttpPost("Filter")]
|
||||
[ProducesResponseType(typeof(PagedResponse<RepoAccount>), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Filter([FromQuery] PageFilter pageFilter, [FromBody] RepoAccountPostFilter repoAccountFilter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var searchResult = await _repoAccountService.ListAll(pageFilter, repoAccountFilter);
|
||||
var result = new PagedResponse<RepoAccount>(searchResult, pageFilter, this.Url, Request, "Filter");
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("Delete/{AccountId}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(500)]
|
||||
[ProducesResponseType(400)]
|
||||
public async Task<IActionResult> DeleteAccountById([FromRoute] int accountId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _repoAccountService.DeleteAsync(accountId);
|
||||
}catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
return Ok($"Delete of Account with Id: {accountId} Successful");
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class RepoCompanyController : RepoControllerBase
|
||||
{
|
||||
private readonly IRepoCompanyService _repoCompanyService;
|
||||
|
||||
public RepoCompanyController(
|
||||
IRepoCompanyService repoCompanyService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_repoCompanyService = repoCompanyService;
|
||||
}
|
||||
|
||||
// GET: api/RepoCompany
|
||||
[ProducesResponseType(typeof(IEnumerable<RepoCompany>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var repoCompanies = await _repoCompanyService.ListAll();
|
||||
return Ok(repoCompanies);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoCompany/5
|
||||
[HttpGet("{id}", Name = "GetRepoCompanyById")]
|
||||
[ProducesResponseType(typeof(RepoCompany), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var repoCompany = await _repoCompanyService.Get(id);
|
||||
return Ok(repoCompany);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/RepoCompany
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(RepoCompany), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<IActionResult> Post([FromBody] RepoCompany repoCompany)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var newRepoCompany = await _repoCompanyService.Save(repoCompany);
|
||||
return Ok(newRepoCompany);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/RepoCompany/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(RepoCompany), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] RepoCompany repoCompany)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (id <= 0 || id != repoCompany.ID)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var currentUser = CreateUserFromAuth();
|
||||
|
||||
var updatedRepoCompany = await _repoCompanyService.Update(repoCompany, currentUser);
|
||||
return Ok(updatedRepoCompany);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class RepoCompanySelectionController : RepoControllerBase
|
||||
{
|
||||
private readonly IRepoCompanySelectionService _repoCompanySelectionService;
|
||||
|
||||
public RepoCompanySelectionController(
|
||||
IRepoCompanySelectionService repoCompanySelectionService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_repoCompanySelectionService = repoCompanySelectionService;
|
||||
}
|
||||
|
||||
// GET: api/RepoCompanySelection
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<RepoCompanySelection>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<ActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var repoCompanySelections = await _repoCompanySelectionService.ListAllAsync();
|
||||
return Ok(repoCompanySelections);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/RepoCompanySelection/5
|
||||
[HttpGet("{id}", Name = "GetRepoCompanySelectionById")]
|
||||
[ProducesResponseType(typeof(RepoCompanySelection), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<ActionResult> Get(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var repoCompanySelection = await _repoCompanySelectionService.GetAsync(id);
|
||||
return Ok(repoCompanySelection);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Claims;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
public class RepoControllerBase : ControllerBase
|
||||
{
|
||||
protected readonly IHttpContextAccessor HttpContextAccessor;
|
||||
|
||||
protected RepoControllerBase(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
HttpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
protected AppUser CreateUserFromAuth()
|
||||
{
|
||||
var userClaimsPrincipal = HttpContextAccessor.HttpContext.User;
|
||||
|
||||
return new AppUser
|
||||
{
|
||||
FirstName = userClaimsPrincipal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.GivenName)?.Value,
|
||||
LastName = userClaimsPrincipal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Surname)?.Value,
|
||||
Email = userClaimsPrincipal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Upn)?.Value,
|
||||
Roles = userClaimsPrincipal.Claims.Where((claim) => claim.Type == ClaimTypes.Role).Select((claim) => claim?.Value).ToArray(),
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetInvokerAsyncMethodName([CallerMemberName] string methodName = null) => methodName;
|
||||
|
||||
}
|
||||
}
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Models.Requests;
|
||||
using LCU.API.Collections.RepoApi.Services.Reporting;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting.Responses;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
[ApiController]
|
||||
public class ReportingController : RepoControllerBase
|
||||
{
|
||||
private readonly ILandmarkReportingService _landmarkReportingService;
|
||||
private readonly IAuctionReportingService _auctionReportingService;
|
||||
private readonly IRepoCompanyReportingService _repoCompanyReportingService;
|
||||
|
||||
public ReportingController(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILandmarkReportingService landmarkReportingService,
|
||||
IAuctionReportingService auctionReportingService,
|
||||
IRepoCompanyReportingService repoCompanyReportingService) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_landmarkReportingService = landmarkReportingService;
|
||||
_auctionReportingService = auctionReportingService;
|
||||
_repoCompanyReportingService = repoCompanyReportingService;
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionAssignedBalance
|
||||
[HttpPost("AuctionAssignedBalance", Name = "AuctionAssignedBalance")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionAssignedBalance([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _auctionReportingService.GetAssignedBalance(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionCountOfAssignedCollateral
|
||||
[HttpPost("AuctionCountOfAssignedCollateral", Name = "AuctionCountOfAssignedCollateral")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionCountOfAssignedCollateral([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _auctionReportingService.GetAssignedCollateralCount(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionSalePrice
|
||||
[HttpPost("AuctionSalePrice", Name = "AuctionSalePrice")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionSalePrice([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _auctionReportingService.GetSalePriceReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionTotalFees
|
||||
[HttpPost("AuctionTotalFees", Name = "AuctionTotalFees")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionTotalFees([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _auctionReportingService.GetAuctionFeeReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionAvgDaysToSold
|
||||
[HttpPost("AuctionAvgDaysToSold", Name = "AuctionAvgDaysToSold")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionAvgDaysToSold([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _auctionReportingService.GetAvgDaysAssignedToSold(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AuctionAvgNetOfFloor
|
||||
[HttpPost("AuctionAvgNetOfFloor", Name = "AuctionAvgNetOfFloor")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAuctionAvgNetOfFloor([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _auctionReportingService.GetAvgNetOfFloor(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/RepoCompanyCountOfAssignedCollateral
|
||||
[HttpPost("RepoCompanyCountOfAssignedCollateral", Name = "RepoCompanyCountOfAssignedCollateral")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRepoCompanyCountOfAssignedCollateral([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _repoCompanyReportingService.GetAssignmentCountReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/RepoCompanyAssignedBalance
|
||||
[HttpPost("RepoCompanyAssignedBalance", Name = "RepoCompanyAssignedBalance")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRepoCompanyAssignedBalance([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _repoCompanyReportingService.GetAssignedBalance(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/RepoCompanyRepoCount
|
||||
[HttpPost("RepoCompanyRepoCount", Name = "RepoCompanyRepoCount")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRepoCompanyRepoCount([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _repoCompanyReportingService.GetRepoedCountReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/RepoCompanyAvgDaysAssignedToRepo
|
||||
[HttpPost("RepoCompanyAvgDaysAssignedToRepo", Name = "RepoCompanyAvgDaysAssignedToRepo")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetRepoCompanyAvgDaysAssignedToRepo([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _repoCompanyReportingService.GetAvgDaysAssignedToRepoed(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AvgDaysDlqWhenAdded
|
||||
[HttpPost("AvgDaysDlqWhenAdded", Name = "AvgDaysDlqWhenAdded")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgDaysDlqWhenAdded([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetAvgDaysDelqWhenAddedReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AvgDaysRepoToSold
|
||||
[HttpPost("AvgDaysRepoToSold", Name = "AvgDaysRepoToSold")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgDaysRepoToSold([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetAvgDaysRepoToSoldReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/CountOfSales
|
||||
[HttpPost("CountOfSales", Name = "CountOfSales")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetCountOfSales([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetCountOfSalesReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/AvgSurplusOrDeficiency
|
||||
[HttpPost("AvgSurplusOrDeficiency", Name = "AvgSurplusOrDeficiency")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetAvgSurplusOrDeficiency([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetAvgSurplusReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/CountOfRedemptions
|
||||
[HttpPost("CountOfRedemptions", Name = "CountOfRedemptions")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetCountOfRedemptions([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetRedemptionCountReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/TotalRedemptionBalance
|
||||
[HttpPost("TotalRedemptionBalance", Name = "TotalRedemptionBalance")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetTotalRedemptionBalance([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetRedemptionBalanceReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/LoanBalanceRepoedCollateral
|
||||
[HttpPost("LoanBalanceInventory", Name = "LoanBalanceInventory")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetLoanBalanceInventory([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _landmarkReportingService.GetRepoBalanceReport(reportingRequest.LowDate, reportingRequest.HighDate);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/IsActiveAccount
|
||||
[HttpPost("IsActiveAccount", Name = "IsActiveAccount")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetIsActiveAccount([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results =
|
||||
await _landmarkReportingService.GetIsActiveAccountReport(reportingRequest.LowDate,
|
||||
reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Reporting/IsClosedAccount
|
||||
[HttpPost("IsClosedAccount", Name = "IsClosedAccount")]
|
||||
[ProducesResponseType(typeof(ReportTableResponse), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetIsClosedAccount([FromBody] ReportingRequest reportingRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results =
|
||||
await _landmarkReportingService.GetIsClosedAccountReport(reportingRequest.LowDate,
|
||||
reportingRequest.HighDate);
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class SavedLocationController : RepoControllerBase
|
||||
{
|
||||
private readonly ISavedLocationService _savedLocationService;
|
||||
|
||||
public SavedLocationController(
|
||||
ISavedLocationService savedLocationService,
|
||||
IHttpContextAccessor httpContextAccessor) :
|
||||
base(httpContextAccessor)
|
||||
{
|
||||
_savedLocationService = savedLocationService;
|
||||
}
|
||||
|
||||
// GET: api/SavedLocation
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<SavedLocation>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var savedLocations = await _savedLocationService.ListAllAsync();
|
||||
return Ok(savedLocations);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/SavedLocation/5
|
||||
[HttpGet("{id}", Name = "GetSavedLocationById")]
|
||||
[ProducesResponseType(typeof(SavedLocation), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var savedLocation = await _savedLocationService.GetAsync(id);
|
||||
return Ok(savedLocation);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// POST: api/SavedLocation
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(SavedLocation), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] SavedLocation savedLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var newSavedLocation = await _savedLocationService.SaveAsync(savedLocation);
|
||||
return Ok(newSavedLocation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/SavedLocation/5
|
||||
[HttpPut("{id}")]
|
||||
[ProducesResponseType(typeof(SavedLocation), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Put(int id, [FromBody] SavedLocation savedLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
if (id <= 0 || id != savedLocation.ID)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var updatedSavedLocation = await _savedLocationService.UpdateAsync(savedLocation);
|
||||
return Ok(updatedSavedLocation);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: api/ApiWithActions/5
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(typeof(SavedLocation), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _savedLocationService.DeleteAsync(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Settings;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class SettingsController : RepoControllerBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public SettingsController(
|
||||
ISettingsService settingsService,
|
||||
IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
// GET: api/Settings
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(Settings), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _settingsService.ListAllSettingsAsync();
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/Settings/[method]
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutAccountType([FromBody] AccountType accountType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(accountType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutAllowedStatusChange([FromBody] AllowedStatusChange statusChange)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(statusChange);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutBodyStyle([FromBody] BodyStyle bodyStyle)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(bodyStyle);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutCleaningLevel([FromBody] CleaningLevel cleaningLevel)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(cleaningLevel);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutCollateralType([FromBody] CollateralType collateralType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(collateralType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutCollateralTypeToVehicleType(
|
||||
[FromBody] CollateralTypeToVehicleType collateralToVehicle)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(collateralToVehicle);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutHoldStatus([FromBody] HoldStatus holdStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(holdStatus);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutLoanCategory([FromBody] LoanCategory loanCategory)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(loanCategory);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutLocationType([FromBody] LocationType locationType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(locationType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AllowedStatusChange), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PutVehicleType([FromBody] VehicleType vehicleType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.UpdateAsync(vehicleType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/Settings/[method]
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostAccountType([FromBody] AccountType accountType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(accountType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostAllowedStatusChange([FromBody] AllowedStatusChange statusChange)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(statusChange);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostBodyStyle([FromBody] BodyStyle bodyStyle)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(bodyStyle);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostCleaningLevel([FromBody] CleaningLevel cleaningLevel)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(cleaningLevel);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostCollateralType([FromBody] CollateralType collateralType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(collateralType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostCollateralTypeToVehicleType(
|
||||
[FromBody] CollateralTypeToVehicleType collateralToVehicle)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(collateralToVehicle);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostHoldStatus([FromBody] HoldStatus holdStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(holdStatus);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostLoanCategory([FromBody] LoanCategory loanCategory)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(loanCategory);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostLocationType([FromBody] LocationType locationType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(locationType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]")]
|
||||
[ProducesResponseType(typeof(AccountType), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> PostVehicleType([FromBody] VehicleType vehicleType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var result = await _settingsService.SaveAsync(vehicleType);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class StatusController : RepoControllerBase
|
||||
{
|
||||
private readonly IEnvironmentView _environmentView;
|
||||
|
||||
public StatusController(IHttpContextAccessor httpContextAccessor, IEnvironmentView environmentView) : base(httpContextAccessor)
|
||||
{
|
||||
_environmentView = environmentView;
|
||||
}
|
||||
|
||||
[HttpGet()]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(500)]
|
||||
public IActionResult Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(_environmentView);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode((int) System.Net.HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Models.Requests;
|
||||
using LCU.API.Collections.RepoApi.Models.Responses;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.FeatureManagement.Mvc;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[FeatureGate(FeatureFlags.EnableControllers)]
|
||||
public class VehicleFloorPricingParametersController : RepoControllerBase
|
||||
{
|
||||
private readonly IVehicleFloorPricingParametersService _parametersService;
|
||||
|
||||
public VehicleFloorPricingParametersController(
|
||||
IVehicleFloorPricingParametersService parametersService,
|
||||
IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
_parametersService = parametersService;
|
||||
}
|
||||
|
||||
// GET: api/VehicleFloorPricingParameters
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<VehicleFloorPricingParameters>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _parametersService.ListAll();
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/VehicleFloorPricingParameters/5
|
||||
[HttpGet("{id}", Name = "GetVehicleFloorPricingParametersById")]
|
||||
[ProducesResponseType(typeof(VehicleFloorPricingParameters), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _parametersService.Get(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut()]
|
||||
[ProducesResponseType(typeof(AuctionHouse), 200)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
||||
public async Task<IActionResult> Put([FromBody] VehicleFloorPricingParametersPutRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState.GetErrorMessages());
|
||||
}
|
||||
|
||||
var updatedParams = await UpdateVehiceFloorPricingParameters(request.vehicleFloorPricingParameters);
|
||||
return Ok(new VehicleFloorPricingParametersPutResponse { vehicleFloorPricingParameters = updatedParams });
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
return NotFound(nfe.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failure in {GetType().Name} at {GetInvokerAsyncMethodName()}");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<VehicleFloorPricingParameters[]> UpdateVehiceFloorPricingParameters(VehicleFloorPricingParameters[] vehicleFloorPricingParameters)
|
||||
{
|
||||
var result = new List<VehicleFloorPricingParameters>();
|
||||
foreach (var param in vehicleFloorPricingParameters)
|
||||
{
|
||||
result.Add(await _parametersService.UpdateAsync(param));
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Settings;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.DataAccess
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class CollectionsDao : DaoClient, ICollectionsDao
|
||||
{
|
||||
private readonly RepoApiConfiguration _configuration;
|
||||
|
||||
public CollectionsDao(RepoApiConfiguration configuration,
|
||||
IHttpClientFactory clientFactory) : base(clientFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<TemenosAccount> RetrieveAccountAsync(string accountNo)
|
||||
{
|
||||
var url = BuildTemenosAccountUrl(accountNo);
|
||||
var accounts = await RetrieveByIdentifierAsync<IReadOnlyCollection<TemenosAccount>>(url, accountNo);
|
||||
|
||||
if (accounts != null && accounts.Any())
|
||||
{
|
||||
return accounts.Where(x => x.AccountIdentifier == accountNo)
|
||||
.OrderByDescending(x => x.OriginalLoanDate).First();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<TemenosHistory>> RetrieveHistoryAsync(int accountId)
|
||||
{
|
||||
var url = _configuration.CollectionsLetterUrl.Replace(":accountId", accountId.ToString());
|
||||
return await RetrieveByIdentifierAsync<IReadOnlyCollection<TemenosHistory>>(url, accountId.ToString());
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<BranchEntry>> RetrieveBranchSetAsync()
|
||||
{
|
||||
var url = _configuration.CoreBranchSetUrl;
|
||||
return await RetrieveAllAsync<IReadOnlyCollection<BranchEntry>>(url);
|
||||
}
|
||||
|
||||
public async Task<TemenosConfiguration> RetrieveTemenosConfigurationAsync()
|
||||
{
|
||||
var url = _configuration.CollectionsConfigUrl;
|
||||
return await RetrieveAllAsync<TemenosConfiguration>(url);
|
||||
}
|
||||
|
||||
public string BuildTemenosAccountUrl(string accountNo)
|
||||
{
|
||||
return _configuration.CollectionAccountUrl + _configuration.OrganizationCode + accountNo;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.DataAccess
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class CoreDao : DaoClient, ICoreDao
|
||||
{
|
||||
private readonly RepoApiConfiguration _configuration;
|
||||
|
||||
public CoreDao(RepoApiConfiguration configuration,
|
||||
IHttpClientFactory clientFactory) : base(clientFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<MemberDetail> RetrieveMemberAsync(string accountNo)
|
||||
{
|
||||
var url = CreateUrlFromAccountNo(_configuration.CoreMemberUrl, accountNo);
|
||||
return await RetrieveByIdentifierAsync<MemberDetail>(url, accountNo);
|
||||
}
|
||||
|
||||
public async Task<LoanDetail> RetrieveLoanAsync(string accountNo)
|
||||
{
|
||||
var url = CreateUrlFromAccountNo(_configuration.CoreLoanUrl, accountNo);
|
||||
return await RetrieveByIdentifierAsync<LoanDetail>(url, accountNo);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<CollateralDetail>> RetrieveCollateralAsync(string accountNo)
|
||||
{
|
||||
var url = CreateUrlFromAccountNo(_configuration.CoreCollateralUrl, accountNo);
|
||||
return await RetrieveByIdentifierAsync<IReadOnlyCollection<CollateralDetail>>(url, accountNo);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<CoreTransaction>> RetrieveTransactionsAsync(string accountNo)
|
||||
{
|
||||
var url = CreateUrlFromAccountNoAndDateRange(_configuration.CoreLoanTransactions, accountNo);
|
||||
return await RetrieveByIdentifierAsync<IReadOnlyCollection<CoreTransaction>>(url, accountNo);
|
||||
}
|
||||
|
||||
private string CreateUrlFromAccountNo(string inputUrl, string accountNo)
|
||||
{
|
||||
var url = inputUrl.Replace(":loanId", accountNo);
|
||||
return url;
|
||||
}
|
||||
|
||||
private string CreateUrlFromAccountNoAndDateRange(string inputUrl, string accountNo)
|
||||
{
|
||||
var url = inputUrl.Replace(":accountNo", accountNo);
|
||||
url = url.Replace(":startDate", DateTimeForUri(DateTime.Now.AddDays(_configuration.TransactionHistoryStartDateOffset)));
|
||||
url = url.Replace(":endDate", DateTimeForUri(DateTime.Now));
|
||||
url += FormatArrayValuesIntoQuery(_configuration.TransactionTypeCodes, _configuration.TransactionTypeCodeQueryPrefix);
|
||||
return url;
|
||||
}
|
||||
|
||||
private string DateTimeForUri(DateTime dateTime)
|
||||
{
|
||||
return dateTime.ToString(_configuration.TransactionHistoryParameterDateFormat);
|
||||
}
|
||||
|
||||
private string FormatArrayValuesIntoQuery(List<string> arrayValues, string queryPrefix)
|
||||
{
|
||||
var returnString = "";
|
||||
foreach (var arrayValue in arrayValues) {
|
||||
returnString += (queryPrefix + arrayValue);
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.DataAccess
|
||||
{
|
||||
public abstract class DaoClient
|
||||
{
|
||||
private readonly IHttpClientFactory _clientFactory;
|
||||
|
||||
protected DaoClient(IHttpClientFactory clientFactory)
|
||||
{
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
public async Task<T> RetrieveByIdentifierAsync<T>(string url, string identifier)
|
||||
{
|
||||
// Temporary log to illustrate this working completely
|
||||
Log.Information($"Retrieve Async called for {typeof(T).Name} with {url}");
|
||||
using (var client = _clientFactory.CreateClient())
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var retrievedObject = JsonConvert.DeserializeObject<T>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
return retrievedObject;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex,$"Error Deserializing {typeof(T).Name} for {identifier}");
|
||||
throw new ServerErrorException();
|
||||
}
|
||||
}
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
Log.Error($"Retrieve {typeof(T).Name} Bad Status Code {response.StatusCode}, for { url}");
|
||||
throw new MissingRequirementException(typeof(T).Name, $"Failed to retrieve {typeof(T).Name} for {identifier}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T> RetrieveAllAsync<T>(string url)
|
||||
{
|
||||
// Temporary log
|
||||
Log.Information($"Retrieve All Async called for {typeof(T).Name} with {url}");
|
||||
using (var client = _clientFactory.CreateClient())
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var retrievedObject = JsonConvert.DeserializeObject<T>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
return retrievedObject;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex,$"Error Deserializing {typeof(T).Name} for {url}");
|
||||
throw new ServerErrorException();
|
||||
}
|
||||
}
|
||||
|
||||
Log.Error($"Retrieve {typeof(T).Name} Bad Status Code {response.StatusCode}, for { url}");
|
||||
throw new MissingRequirementException(typeof(T).Name, $"Failed to retrieve {typeof(T).Name} for {url}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Settings;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.DataAccess
|
||||
{
|
||||
public interface ICollectionsDao
|
||||
{
|
||||
Task<TemenosAccount> RetrieveAccountAsync(string accountNo);
|
||||
Task<IReadOnlyCollection<TemenosHistory>> RetrieveHistoryAsync(int accountId);
|
||||
Task<IReadOnlyCollection<BranchEntry>> RetrieveBranchSetAsync();
|
||||
Task<TemenosConfiguration> RetrieveTemenosConfigurationAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.DataAccess
|
||||
{
|
||||
public interface ICoreDao
|
||||
{
|
||||
Task<MemberDetail> RetrieveMemberAsync(string accountNo);
|
||||
Task<LoanDetail> RetrieveLoanAsync(string accountNo);
|
||||
Task<IReadOnlyCollection<CollateralDetail>> RetrieveCollateralAsync(string accountNo);
|
||||
Task<IReadOnlyCollection<CoreTransaction>> RetrieveTransactionsAsync(string accountNo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
|
||||
COPY bin/Debug/netcoreapp2.2/publish App/
|
||||
WORKDIR /App
|
||||
ENV ASPNETCORE_ENVIRONMENT=Development
|
||||
ENTRYPOINT ["dotnet", "LCU.API.Collections.RepoApi.dll"]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class AuctionOnHoldChecker : IAlertChecker
|
||||
{
|
||||
private const string RehoStatus = "REHO";
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var newEventAlerts = new List<EventAlert>();
|
||||
foreach (var auctionAssignment in slip.AuctionAssignments)
|
||||
{
|
||||
if (
|
||||
slip.UpdatedRepoAccount.Status == RehoStatus
|
||||
&& auctionAssignment?.HoldStatus != null
|
||||
&& auctionAssignment.HoldStatus > 0)
|
||||
{
|
||||
newEventAlerts.Add(
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Auction On Hold",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return newEventAlerts.Count > 0 ? newEventAlerts : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class AuctionVinDifferentChecker : IAlertChecker
|
||||
{
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var newEventAlerts = new List<EventAlert>();
|
||||
var repoManagerVins = slip.UpdatedRepoAccount.GetCollateralVinList();
|
||||
foreach (var auctionAssignment in slip.AuctionAssignments)
|
||||
{
|
||||
if (auctionAssignment?.AutoIMS?.VIN != null
|
||||
&& !repoManagerVins.Contains(auctionAssignment.AutoIMS.VIN.Trim().ToUpper()))
|
||||
{
|
||||
newEventAlerts.Add(
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Auction VIN Different",
|
||||
SubMessage = FormatSubMessage(auctionAssignment.AutoIMS.VIN.Trim().ToUpper(), repoManagerVins),
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return newEventAlerts.Count > 0 ? newEventAlerts : null;
|
||||
}
|
||||
|
||||
private static string FormatSubMessage(string auctionAssignmentVin, IReadOnlyCollection<string> vins)
|
||||
{
|
||||
if (vins.Count() == 1)
|
||||
{
|
||||
return
|
||||
$"Auction Assignment VIN: {auctionAssignmentVin} differs from RepoManager VIN: {vins.First()}";
|
||||
}
|
||||
|
||||
var repoVins = vins.Aggregate((x, y) => x + ", " + y);
|
||||
return $"Auction Assignment VIN: {auctionAssignmentVin} is not among RepoManager VINs: {repoVins}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class CreditAdjustmentChecker : TransactionHistoryChecker
|
||||
{
|
||||
private readonly List<string> _watchedStatuses = new List<string> { "O4R", "REPO", "SOLD" };
|
||||
private readonly List<string> _watchedTransactionTypes = new List<string> { "PDSB", "RPMT" };
|
||||
private const string Message = "Credit Adjustment Received";
|
||||
|
||||
public override List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes));
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class DebitAdjustmentChecker : TransactionHistoryChecker
|
||||
{
|
||||
private readonly List<string> _watchedStatuses = new List<string> { "O4R", "REPO", "SOLD" };
|
||||
private readonly List<string> _watchedTransactionTypes = new List<string> { "CHST", "PRCT" };
|
||||
private const string Message = "Debit Adjustment Received";
|
||||
|
||||
public override List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes));
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class DocumentNotFoundChecker : IAlertChecker
|
||||
{
|
||||
private readonly string _document;
|
||||
private readonly string _ilActionCode;
|
||||
private readonly string _status;
|
||||
private readonly string _wiActionCode;
|
||||
private const string StateWisconsin = "WI";
|
||||
private const string StateIllinois = "IL";
|
||||
|
||||
public DocumentNotFoundChecker(string document, string wiActionCode, string ilActionCode = null,
|
||||
string status = null)
|
||||
{
|
||||
_document = document;
|
||||
_wiActionCode = wiActionCode.Trim().ToUpper();
|
||||
_ilActionCode = ilActionCode?.Trim().ToUpper();
|
||||
_status = status?.Trim().ToUpper();
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
if (slip.Letters == null || slip.Member?.Address == null ||
|
||||
(_status != null && slip.UpdatedRepoAccount.Status != _status ||
|
||||
slip.UpdatedRepoAccount.Bankruptcy != null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var isLetterMissing = false;
|
||||
if (slip.Member.Address.State == StateWisconsin)
|
||||
{
|
||||
isLetterMissing = slip.Letters.ToList().Find(x => x.ActionCode == _wiActionCode) == null;
|
||||
}
|
||||
else if (_ilActionCode != null && slip.Member.Address.State == StateIllinois)
|
||||
{
|
||||
isLetterMissing = slip.Letters.ToList().Find(x => x.ActionCode == _ilActionCode) == null;
|
||||
}
|
||||
|
||||
if (isLetterMissing)
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = _document + " Not Found",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using EventAlert = LCU.Shared.Collections.RepoApiLib.Models.EventAlert;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public interface IAlertChecker
|
||||
{
|
||||
List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class NoticeOfSurplusDeficiencyNotSentChecker : IAlertChecker
|
||||
{
|
||||
private const int OwnershipTypeImportant = 6;
|
||||
private const int DaysBeforeConsideredNotSent = 2;
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var updatedRepoAccount = slip.UpdatedRepoAccount;
|
||||
if (updatedRepoAccount.RefundStatus?.RefundsReceivedDate == null || updatedRepoAccount.DateClosed != null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var letterSentBy = DateTime.Now.AddDays(-DaysBeforeConsideredNotSent).Date;
|
||||
if (updatedRepoAccount.RefundStatus.RefundsReceivedDate < letterSentBy && updatedRepoAccount.Bankruptcy == null)
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = updatedRepoAccount.RepoId,
|
||||
AccountNo = updatedRepoAccount.AccountNo,
|
||||
Message = "Notice of Surplus Deficiency Letter Not Sent",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now,
|
||||
EmailRecipient = "[email protected]",
|
||||
EmailBody = CreateEmailBody(updatedRepoAccount)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string CreateEmailBody(RepoAccount repoAccount)
|
||||
{
|
||||
var message = "Notice of Surplus Deficiency Not Sent.<br/><br/>";
|
||||
var member = repoAccount.Borrowers?.First(b => b.OwnershipType == OwnershipTypeImportant);
|
||||
var memberName = "Member Name: " + member?.IndividualName + "<br/>";
|
||||
var accountNo = "Account No: " + repoAccount.AccountNo + "<br/>";
|
||||
return string.Concat(message, memberName, accountNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class O4rNoRepoChecker : IAlertChecker
|
||||
{
|
||||
private readonly RepoType _repoType;
|
||||
private const string O4RStatus = "O4R";
|
||||
private const string EmailDestination = "[email protected]";
|
||||
|
||||
public O4rNoRepoChecker(RepoType repoType)
|
||||
{
|
||||
_repoType = repoType;
|
||||
}
|
||||
|
||||
private int GetDayCount()
|
||||
{
|
||||
switch (_repoType)
|
||||
{
|
||||
case RepoType.Voluntary: return 2;
|
||||
case RepoType.Involuntary: return 60;
|
||||
case RepoType.PreSkip: return 31;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetRepeatDayCount()
|
||||
{
|
||||
switch (_repoType)
|
||||
{
|
||||
case RepoType.Voluntary: return 1;
|
||||
case RepoType.Involuntary: return 30;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldSendRepeatedAlert(int daysInStatus, int dayCount, int repeatCount)
|
||||
{
|
||||
return (
|
||||
daysInStatus > dayCount
|
||||
&& (repeatCount > 0)
|
||||
&& ((daysInStatus - dayCount) % repeatCount == 0)
|
||||
);
|
||||
}
|
||||
|
||||
public bool IsRepoAssigned(RepoAccount repoAccount)
|
||||
{
|
||||
foreach (var collateral in repoAccount.Collaterals)
|
||||
{
|
||||
if (collateral.RepoCompanySelections?.FirstOrDefault(x => x.RepoCompanyAssignmentDate != null && x.RepoType == _repoType) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var daysInStatus = slip.UpdatedRepoAccount.GetDaysInStatus(O4RStatus);
|
||||
if (daysInStatus > 0 && slip.UpdatedRepoAccount.Status == O4RStatus)
|
||||
{
|
||||
// In order to reduce duplicate notifications, do not create a notification if no repo is assigned.
|
||||
// An 'action needed' item will be created for O4R No Repo if no repo is assigned. A notification is redundant.
|
||||
if (daysInStatus < GetDayCount() || !IsRepoAssigned(slip.UpdatedRepoAccount))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (daysInStatus == GetDayCount() || ShouldSendRepeatedAlert(daysInStatus, GetDayCount(), GetRepeatDayCount()))
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "O4R No Repo",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now,
|
||||
EmailRecipient = _repoType == RepoType.PreSkip
|
||||
? EmailDestination
|
||||
: null,
|
||||
EmailBody = _repoType == RepoType.PreSkip
|
||||
? CreateEmailBody(slip.UpdatedRepoAccount)
|
||||
: null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string CreateEmailBody(RepoAccount repoAccount)
|
||||
{
|
||||
var message = "PreSkip has ended, please file a Skip Claim.<br/><br/>";
|
||||
var member = repoAccount.Borrowers?.First(b => b.OwnershipType == 6);
|
||||
var memberName = "Member Name: " + member?.IndividualName + "<br/>";
|
||||
var accountNo = "Account No: " + repoAccount.AccountNo + "<br/>";
|
||||
var collateral = repoAccount.Collaterals.FirstOrDefault(c => c.RepoCompanySelections?.FirstOrDefault(x => x.RepoType == _repoType) != null);
|
||||
var year = "Year: " + collateral.Year + "<br/>";
|
||||
var make = "Make: " + collateral.Make + "<br/>";
|
||||
var model = "Model: " + collateral.Model + "<br/>";
|
||||
var vin = "VIN: " + collateral.Vin + "<br/>";
|
||||
|
||||
return string.Concat(message, memberName, accountNo, year, make, model, vin);
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class PaymentReceivedChecker : TransactionHistoryChecker
|
||||
{
|
||||
private readonly List<string> _watchedStatuses = new List<string> { "O4R", "REPO", "SOLD" };
|
||||
private readonly List<string> _watchedTransactionTypes = new List<string> { "COFR", "POFF", "EXT", "SPMT", "XPMT" };
|
||||
private const string EmailDestination = "[email protected]";
|
||||
|
||||
public override List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var newEventAlerts = new List<EventAlert>();
|
||||
var results = GetRecentTransactionsFromType(_watchedTransactionTypes, slip.TransactionHistory);
|
||||
if (results != null && _watchedStatuses.Contains(slip.UpdatedRepoAccount.Status))
|
||||
{
|
||||
var dueDate = slip.Loan?.NextDueDate.ToString(CultureInfo.CurrentCulture) ?? "Unknown";
|
||||
foreach (var result in results)
|
||||
{
|
||||
newEventAlerts.Add(new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Payment Received",
|
||||
SubMessage = FormatTransactionMessage(result),
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now,
|
||||
EmailRecipient = EmailDestination,
|
||||
EmailBody = CreateEmailBody(slip.UpdatedRepoAccount, result, dueDate)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newEventAlerts.Count > 0 ? newEventAlerts : null;
|
||||
}
|
||||
|
||||
private string CreateEmailBody(RepoAccount repoAccount, CoreTransaction coreTransaction, string nextDueDate)
|
||||
{
|
||||
var message = "Loan Payment<br/><br/>";
|
||||
var member = repoAccount.Borrowers?.First(b => b.OwnershipType == 6);
|
||||
var memberName = "Member Name: " + member?.IndividualName + "<br/>";
|
||||
var accountNo = "Account No: " + repoAccount.AccountNo + "<br/>";
|
||||
var paymentAmount = "Payment Amount: " + coreTransaction.amount + "<br/>";
|
||||
var nextDueDateString = "Next Due Date: " + nextDueDate + "<br/>";
|
||||
var loanBalance = "Loan Balance: " + coreTransaction.runningBalance + "<br/>";
|
||||
|
||||
return string.Concat(message, memberName, accountNo, paymentAmount, nextDueDateString, loanBalance);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class RedemptionEndedNoTitleOrderedChecker : IAlertChecker
|
||||
{
|
||||
private const string RepoStatus = "REPO";
|
||||
private const int RedemptionExpiredDaysToWait = 7;
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
if (slip.UpdatedRepoAccount.Status.ToUpper() == RepoStatus &&
|
||||
DateTime.Now.Date > slip.UpdatedRepoAccount.RedemptionExpiredDate?.AddDays(RedemptionExpiredDaysToWait) &&
|
||||
slip.UpdatedRepoAccount.Collaterals.FirstOrDefault(x => x.RetitledInLandmarkName == null) != null)
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Redemption Ended No Title Ordered",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class RepoNotMovedChecker : IAlertChecker
|
||||
{
|
||||
private readonly int _dayCount;
|
||||
private const string RepoStatus = "REPO";
|
||||
private const int DaysBeforeSendEmail = 14;
|
||||
private const string EmailDestination = "[email protected]";
|
||||
|
||||
public RepoNotMovedChecker(int dayCount = 30)
|
||||
{
|
||||
_dayCount = dayCount;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var daysInStatus = slip.UpdatedRepoAccount.GetDaysInStatus(RepoStatus);
|
||||
if (slip.UpdatedRepoAccount.Status.ToUpper() != RepoStatus ||
|
||||
slip.UpdatedRepoAccount.Collaterals == null || !slip.UpdatedRepoAccount.Collaterals.Any()
|
||||
|| daysInStatus < _dayCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var collateral in slip.UpdatedRepoAccount.Collaterals)
|
||||
{
|
||||
if (collateral.AuctionAssignmentToCollaterals != null &&
|
||||
collateral.AuctionAssignmentToCollaterals.Any())
|
||||
{
|
||||
var assignment = collateral.AuctionAssignmentToCollaterals
|
||||
.Where(x => x.AuctionAssignment != null && x.AuctionAssignment.DateSubmitted == null)
|
||||
.ToList();
|
||||
|
||||
if (assignment.Any() && daysInStatus >= _dayCount)
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Repo Not Moved",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now,
|
||||
EmailRecipient = daysInStatus == DaysBeforeSendEmail
|
||||
? EmailDestination
|
||||
: null,
|
||||
EmailBody = daysInStatus == DaysBeforeSendEmail
|
||||
? string.Format("{0} Account status REPO, Not Moved for extended time.", slip.UpdatedRepoAccount.AccountNo)
|
||||
: null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using EventAlert = LCU.Shared.Collections.RepoApiLib.Models.EventAlert;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class RepoNotSoldChecker : IAlertChecker
|
||||
{
|
||||
private readonly int _dayCount;
|
||||
private readonly int _repeatDayCount;
|
||||
private const string RepoStatus = "REPO";
|
||||
private const string DestinationEmail = "[email protected]";
|
||||
|
||||
public RepoNotSoldChecker(int dayCount = 30, int repeatDayCount = 0)
|
||||
{
|
||||
_dayCount = dayCount;
|
||||
_repeatDayCount = repeatDayCount;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var status = slip.UpdatedRepoAccount.Status.ToUpper().Trim();
|
||||
var days = slip.UpdatedRepoAccount.GetDaysInStatus(status);
|
||||
if (status == RepoStatus && days > 0)
|
||||
{
|
||||
if ((days == _dayCount) || days >= _dayCount && ((days - _dayCount) % _repeatDayCount == 0))
|
||||
{
|
||||
var member = slip.UpdatedRepoAccount.Borrowers?.First(b => b.OwnershipType == 6);
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Repo Not Sold",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now,
|
||||
EmailRecipient = DestinationEmail,
|
||||
EmailBody = string.Format("{0} {1} Account status REPO, Not Sold for extended time.",
|
||||
slip.UpdatedRepoAccount.AccountNo, member?.IndividualName)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class RtcLetterNotFoundChecker : IAlertChecker
|
||||
{
|
||||
private readonly string _document;
|
||||
private readonly IEnumerable<string> _actionCodes;
|
||||
private const string O4RStatus = "O4R";
|
||||
private const string StateWisconsin = "WI";
|
||||
|
||||
public RtcLetterNotFoundChecker(string document, IEnumerable<string> actionCodes)
|
||||
{
|
||||
_document = document;
|
||||
_actionCodes = actionCodes.Select(code => code.Trim().ToUpper());
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
if (slip.Member?.Address == null || slip.Member.Address.State != StateWisconsin || slip.Letters == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var collateral in slip.UpdatedRepoAccount.Collaterals)
|
||||
{
|
||||
if (collateral.RepoCompanySelections?.FirstOrDefault(x => x.RepoType == RepoType.Voluntary) != null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var letters = slip.Letters;
|
||||
if (IsLetterMissing(letters) && (slip.UpdatedRepoAccount.Status.ToUpper().Trim() == O4RStatus))
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = _document + " Not Found",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsLetterMissing(IEnumerable<RepoLetter> letters)
|
||||
{
|
||||
var sentLetterCodes = letters.Select(x => x.ActionCode).ToList();
|
||||
foreach (var actionCode in _actionCodes)
|
||||
{
|
||||
if (sentLetterCodes.Contains(actionCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class SaleFundsReceivedChecker : TransactionHistoryChecker
|
||||
{
|
||||
private readonly List<string> _watchedStatuses = null;
|
||||
private readonly List<string> _watchedTransactionTypes = new List<string> { "PRCT" };
|
||||
private const string Message = "Sale Funds Received";
|
||||
|
||||
public override List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes));
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class SkipEndChecker : IAlertChecker
|
||||
{
|
||||
private readonly int _dayCount;
|
||||
private const string O4RStatus = "O4R";
|
||||
|
||||
public SkipEndChecker(int dayCount = 30)
|
||||
{
|
||||
_dayCount = dayCount;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
if (slip.UpdatedRepoAccount.Status == O4RStatus
|
||||
&& slip.UpdatedRepoAccount.GetDaysInStatus(O4RStatus) >= _dayCount
|
||||
&& slip.UpdatedRepoAccount.IsCurrentlySkipClaim())
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Skip Claim Ended",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class SkipNotPaidChecker : IAlertChecker
|
||||
{
|
||||
private readonly int _dayCount;
|
||||
private readonly int _repeatDayCount;
|
||||
private const string O4RStatus = "O4R";
|
||||
private const string RepoSubStatusPaidSkip = "PSKP";
|
||||
|
||||
|
||||
public SkipNotPaidChecker(int dayCount = 30, int repeatDayCount = 0)
|
||||
{
|
||||
_dayCount = dayCount;
|
||||
_repeatDayCount = repeatDayCount;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var days = slip.UpdatedRepoAccount.GetDaysInStatus(O4RStatus);
|
||||
if (slip.UpdatedRepoAccount.Status == O4RStatus
|
||||
&& days >= _dayCount && slip.UpdatedRepoAccount.IsCurrentlySkipClaim())
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(slip.UpdatedRepoAccount.SubStatus) &&
|
||||
slip.UpdatedRepoAccount.SubStatus.Trim().ToUpper() == RepoSubStatusPaidSkip)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((days == _dayCount) || days >= _dayCount && ((days - _dayCount) % _repeatDayCount == 0))
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Skip Not Paid",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class SkipPaidChecker : IAlertChecker
|
||||
{
|
||||
public const string RepoSubStatusPaidSkip = "PSKP";
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
if (slip.UpdatedRepoAccount != null && !string.IsNullOrWhiteSpace(slip.UpdatedRepoAccount.SubStatus) &&
|
||||
slip.UpdatedRepoAccount.SubStatus.Trim().ToUpper() == RepoSubStatusPaidSkip &&
|
||||
slip.UpdatedRepoAccount.Status.ToUpper().Trim() != RepoSubStatusPaidSkip)
|
||||
{
|
||||
slip.UpdatedRepoAccount.Status = RepoSubStatusPaidSkip; // TODO Does this get saved? Is it ok to do? (NO)
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Skip Claim Paid",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class SoldNoProceedsChecker : IAlertChecker
|
||||
{
|
||||
private const int DaysToWaitForSold = 5;
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
foreach (var auctionAssignment in slip.AuctionAssignments)
|
||||
{
|
||||
if (
|
||||
auctionAssignment?.AutoIMS?.SoldDate != null
|
||||
&& auctionAssignment.AutoIMS.SoldDate < DateTime.Now.AddDays(-DaysToWaitForSold)
|
||||
&& auctionAssignment.AutoIMS.FundsSent == null)
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Sold No Proceeds",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class StatusChangeChecker : IAlertChecker
|
||||
{
|
||||
private readonly string _watchStatus;
|
||||
private readonly IEnumerable<string> _fromStatusList;
|
||||
private const string NoStatus = "";
|
||||
|
||||
public StatusChangeChecker(string watchStatus, IEnumerable<string> fromStatusList = null)
|
||||
{
|
||||
_watchStatus = watchStatus.Trim().ToUpper();
|
||||
_fromStatusList = fromStatusList;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var originalStatus = slip.ExistingRepoAccount?.Status.ToUpper().Trim();
|
||||
var updatedStatus = slip.UpdatedRepoAccount.Status.ToUpper().Trim();
|
||||
|
||||
// new account.
|
||||
if (originalStatus == null)
|
||||
{
|
||||
originalStatus = NoStatus;
|
||||
}
|
||||
|
||||
if (updatedStatus == _watchStatus
|
||||
&& originalStatus != _watchStatus
|
||||
&& (_fromStatusList == null || _fromStatusList.Contains(originalStatus)))
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = "Status Changed to " + _watchStatus,
|
||||
SubMessage =
|
||||
$"Previously {originalStatus}. I confirm that I have updated the Status to {updatedStatus} in RDN.",
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.Extensions;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.API.Collections.RepoApi.Extensions;
|
||||
using EventAlert = LCU.Shared.Collections.RepoApiLib.Models.EventAlert;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public class TimeInStatusChecker : IAlertChecker
|
||||
{
|
||||
private readonly string _checkStatus;
|
||||
private readonly int _dayCount;
|
||||
private readonly int _repeatDayCount;
|
||||
|
||||
public TimeInStatusChecker(string checkStatus, int dayCount = 30, int repeatDayCount = 0)
|
||||
{
|
||||
_checkStatus = checkStatus;
|
||||
_dayCount = dayCount;
|
||||
_repeatDayCount = repeatDayCount;
|
||||
}
|
||||
|
||||
public List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip)
|
||||
{
|
||||
var status = slip.UpdatedRepoAccount.Status.ToUpper().Trim();
|
||||
var days = slip.UpdatedRepoAccount.GetDaysInStatus(status);
|
||||
if (days <= 0 || status != _checkStatus)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((days == _dayCount) || days >= _dayCount && ((days - _dayCount) % _repeatDayCount == 0))
|
||||
{
|
||||
return new List<EventAlert>
|
||||
{
|
||||
new EventAlert()
|
||||
{
|
||||
RepoId = slip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = slip.UpdatedRepoAccount.AccountNo,
|
||||
Message = days + " days in " + _checkStatus,
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Domain.Processing;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.AlertChecker
|
||||
{
|
||||
public abstract class TransactionHistoryChecker : IAlertChecker
|
||||
{
|
||||
private readonly int _daysInPastToSearch;
|
||||
|
||||
protected TransactionHistoryChecker(int daysInPastToSearch = 3)
|
||||
{
|
||||
_daysInPastToSearch = daysInPastToSearch;
|
||||
}
|
||||
|
||||
public abstract List<EventAlert> CheckForAlert(RepoAccountProcessingSlip slip);
|
||||
|
||||
protected List<EventAlert> StandardizedCheckForAlert(AlertProcessingSlip alertProcessingSlip)
|
||||
{
|
||||
var newEventAlerts = new List<EventAlert>();
|
||||
var results = GetRecentTransactionsFromType
|
||||
(
|
||||
alertProcessingSlip.WatchedTransactionTypes,
|
||||
alertProcessingSlip.AccountProcessingSlip.TransactionHistory
|
||||
);
|
||||
if (results != null && AccountStatusIsQualified(alertProcessingSlip.WatchedStatusTypes,
|
||||
alertProcessingSlip.AccountProcessingSlip.UpdatedRepoAccount.Status))
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
newEventAlerts.Add(new EventAlert()
|
||||
{
|
||||
RepoId = alertProcessingSlip.AccountProcessingSlip.UpdatedRepoAccount.RepoId,
|
||||
AccountNo = alertProcessingSlip.AccountProcessingSlip.UpdatedRepoAccount.AccountNo,
|
||||
Message = alertProcessingSlip.Message,
|
||||
SubMessage = FormatTransactionMessage(result),
|
||||
CreatedBy = "Batch",
|
||||
DateAdded = DateTime.Now
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return newEventAlerts.Count > 0 ? newEventAlerts : null;
|
||||
}
|
||||
|
||||
private static bool AccountStatusIsQualified(List<string> watchedStatusTypes, string status)
|
||||
{
|
||||
return watchedStatusTypes?.Contains(status.ToUpper()) ?? true;
|
||||
}
|
||||
|
||||
public static string FormatTransactionMessage(CoreTransaction transaction)
|
||||
{
|
||||
var message = "";
|
||||
if (transaction.transactionTypeCode.ToLower().Contains("undo"))
|
||||
{
|
||||
message += "***TRANSACTION UNDONE*** - ";
|
||||
}
|
||||
|
||||
return message +
|
||||
$"Transaction Type: \"{transaction.transactionTypeCode}\" | Posting: {transaction.postingDate} | Transaction: {transaction.amount?.ToString("C2")} | Balance: {transaction.runningBalance?.ToString("C2")} | Description: \"{transaction.transactionTypeDescription}\"";
|
||||
}
|
||||
|
||||
protected List<CoreTransaction> GetRecentTransactionsFromType(List<string> transactionTypes, IReadOnlyCollection<CoreTransaction> transactions)
|
||||
{
|
||||
var returnItems = new List<CoreTransaction>();
|
||||
|
||||
if (transactions != null && transactions.Any())
|
||||
{
|
||||
foreach (var transactionType in transactionTypes)
|
||||
{
|
||||
returnItems.AddRange(GetRecentTransactionsFromType(transactionType, _daysInPastToSearch, transactions));
|
||||
}
|
||||
}
|
||||
|
||||
return returnItems.Count > 0 ? returnItems : null;
|
||||
}
|
||||
|
||||
private static IEnumerable<CoreTransaction> GetRecentTransactionsFromType(string transactionType, int daysInPast, IReadOnlyCollection<CoreTransaction> transactions)
|
||||
{
|
||||
return (
|
||||
from transaction in transactions
|
||||
where (
|
||||
transaction.postingDate > DateTime.Now.AddDays(-daysInPast)
|
||||
&& transaction.transactionTypeCode.ToLower().Contains(transactionType.ToLower()
|
||||
)
|
||||
)
|
||||
select transaction
|
||||
).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.Extensions
|
||||
{
|
||||
public static class RepoAccountExtensions
|
||||
{
|
||||
public static bool HasAnyCollateral(this RepoAccount repoAccount)
|
||||
{
|
||||
return repoAccount?.Collaterals != null && repoAccount.Collaterals.Any();
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<string> GetCollateralVinList(this RepoAccount repoAccount)
|
||||
{
|
||||
if (repoAccount?.Collaterals != null && repoAccount.Collaterals.Any())
|
||||
{
|
||||
return repoAccount.Collaterals.Select(x => x.Vin?.Trim().ToUpper()).ToList();
|
||||
}
|
||||
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<int> GetUniqueAuctionAssignmentIds(this RepoAccount repoAccount)
|
||||
{
|
||||
var aaList = new List<int>();
|
||||
if (repoAccount?.Collaterals == null || !repoAccount.Collaterals.Any()) return aaList;
|
||||
|
||||
foreach (var collateral in repoAccount.Collaterals)
|
||||
{
|
||||
if (collateral.AuctionAssignmentToCollaterals?.Any() != true) continue;
|
||||
|
||||
foreach (var toCollateral in collateral.AuctionAssignmentToCollaterals)
|
||||
{
|
||||
aaList.Add(toCollateral.AuctionAssignmentId);
|
||||
}
|
||||
}
|
||||
|
||||
return aaList;
|
||||
}
|
||||
|
||||
public static int GetDaysInStatus(this RepoAccount repoAccount, string status)
|
||||
{
|
||||
if (repoAccount?.RepoStatusChanges == null || !repoAccount.RepoStatusChanges.Any())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lastStatus = repoAccount.RepoStatusChanges.LastOrDefault(x => x.StatusTo == status);
|
||||
if (lastStatus != null)
|
||||
{
|
||||
return (DateTime.Now - lastStatus.ChangedDate).Days;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static bool IsCurrentlySkipClaim(this RepoAccount updatedRepoAccount)
|
||||
{
|
||||
foreach (var collateral in updatedRepoAccount.Collaterals)
|
||||
{
|
||||
if (collateral.RepoCompanySelections == null || !collateral.RepoCompanySelections.Any()) continue;
|
||||
if (collateral.RepoCompanySelections?.OrderByDescending(x => x.DateTimeCreated).First().RepoType ==
|
||||
RepoType.Skip)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.Extensions
|
||||
{
|
||||
public static class TemenosAccountExtensions
|
||||
{
|
||||
public const string UserStatusFieldOld = "User Status Code 1";
|
||||
public const int OpenCaseStatus = 33;
|
||||
public const int CaseTypeRepossession = 7;
|
||||
|
||||
public static bool HasOldIntellectStatus(this TemenosAccount recoveryAccount)
|
||||
{
|
||||
return recoveryAccount?.CustomFieldMap != null
|
||||
&& recoveryAccount.CustomFieldMap.ContainsKey(UserStatusFieldOld)
|
||||
&& !string.IsNullOrWhiteSpace(recoveryAccount.CustomFieldMap[UserStatusFieldOld]);
|
||||
}
|
||||
|
||||
/**
|
||||
* CaseId of 0 corresponds to "An open case with a valid RepoStatus"
|
||||
*/
|
||||
public static bool HasRecoveryCase(this TemenosAccount recoveryAccount, int caseId)
|
||||
{
|
||||
if (recoveryAccount?.Cases == null || !recoveryAccount.Cases.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return caseId == 0 ? recoveryAccount.HasOpenRecoveryCase() : recoveryAccount.HasSpecificCase(caseId);
|
||||
}
|
||||
|
||||
public static bool HasOpenRecoveryCase(this TemenosAccount recoveryAccount)
|
||||
{
|
||||
TemenosCase selectedCase = null;
|
||||
|
||||
if (recoveryAccount?.Cases == null || !recoveryAccount.Cases.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedCase = recoveryAccount.Cases
|
||||
.FirstOrDefault(x => x.CaseStatusId == OpenCaseStatus && x.RepoStatus != RepoStatus.UNKNOWN);
|
||||
|
||||
return (selectedCase != null);
|
||||
}
|
||||
|
||||
public static bool HasSpecificCase(this TemenosAccount recoveryAccount, int caseId)
|
||||
{
|
||||
TemenosCase selectedCase = null;
|
||||
|
||||
if (recoveryAccount?.Cases == null || !recoveryAccount.Cases.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedCase = recoveryAccount.Cases.FirstOrDefault(x => x.CaseId == caseId);
|
||||
return (selectedCase != null);
|
||||
}
|
||||
|
||||
public static TemenosCase FindRecoveryCaseOrDefault(this TemenosAccount recoveryAccount, int caseId)
|
||||
{
|
||||
TemenosCase selectedCase = null;
|
||||
|
||||
if (recoveryAccount?.Cases == null || !recoveryAccount.Cases.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (caseId > 0)
|
||||
{
|
||||
selectedCase = recoveryAccount.Cases.FirstOrDefault(x => x.CaseId == caseId);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedCase = recoveryAccount.Cases
|
||||
.Where(x => x.CaseStatusId == OpenCaseStatus && x.RepoStatus != RepoStatus.UNKNOWN
|
||||
&& x.CaseTypeId == CaseTypeRepossession && x.RepoStatus != RepoStatus.INELIGIBLE)
|
||||
.OrderByDescending(x => x.CreatedDate).FirstOrDefault();
|
||||
}
|
||||
|
||||
return selectedCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Domain.FloorPricing.Strategies;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.FloorPricing
|
||||
{
|
||||
public class FloorPricingStrategyFactory : IFloorPricingStrategyFactory
|
||||
{
|
||||
public FloorPricingStrategyFactory()
|
||||
{
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<IFloorPricingStrategy> GetFloorPricingStrategies()
|
||||
{
|
||||
return new List<IFloorPricingStrategy>()
|
||||
{
|
||||
new AutoImsSuggestedValue(),
|
||||
new NadaMmrConditionValue()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.FloorPricing
|
||||
{
|
||||
public interface IFloorPricingStrategy
|
||||
{
|
||||
|
||||
FloorPriceCalculationResult CalculateFloorPrice(FloorPriceCalculationParams fpcParams);
|
||||
int GetStrategyWeight();
|
||||
// Requirements and Qualifications
|
||||
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.FloorPricing
|
||||
{
|
||||
public interface IFloorPricingStrategyFactory
|
||||
{
|
||||
IReadOnlyCollection<IFloorPricingStrategy> GetFloorPricingStrategies();
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.FloorPricing.Strategies
|
||||
{
|
||||
/**
|
||||
* This strategy uses the suggested price from the ImsAutoFileData entry for the auction.
|
||||
*/
|
||||
public class AutoImsSuggestedValue : IFloorPricingStrategy
|
||||
{
|
||||
public AutoImsSuggestedValue() { }
|
||||
|
||||
public FloorPriceCalculationResult CalculateFloorPrice(FloorPriceCalculationParams fpcParams)
|
||||
{
|
||||
var result = new FloorPriceCalculationResult()
|
||||
{
|
||||
StrategyName = this.GetType().FullName,
|
||||
Success = false
|
||||
};
|
||||
|
||||
if (fpcParams?.ImsAutoFileData?.AuctionGuideBookPrice != null)
|
||||
{
|
||||
// Price must be rounded to nearest $5 per business.
|
||||
var inputPrice = (double)(fpcParams.ImsAutoFileData.AuctionGuideBookPrice ?? 0);
|
||||
result.FloorPrice = (decimal) (Math.Round( inputPrice / 5.0) * 5);
|
||||
result.Success = result.FloorPrice > 0;
|
||||
result.FailureReason = result.Success ? "" : "Failed attempt to use auction guide book price.";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Success = false;
|
||||
result.FailureReason = "Auction Guide Book Price missing.";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetStrategyWeight()
|
||||
{
|
||||
return 0; // This is NOT the preferred strategy.
|
||||
}
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.WebServices.NADA;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Enumerable = System.Linq.Enumerable;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.FloorPricing.Strategies
|
||||
{
|
||||
/**
|
||||
* This strategy uses the AutoIms MMR values, the values from Nada, and the condition from AutoIms along
|
||||
* with a specific formula, to calculate a floor.
|
||||
*/
|
||||
public class NadaMmrConditionValue : IFloorPricingStrategy
|
||||
{
|
||||
|
||||
public FloorPriceCalculationResult CalculateFloorPrice(FloorPriceCalculationParams fpcParams)
|
||||
{
|
||||
var result = new FloorPriceCalculationResult()
|
||||
{
|
||||
StrategyName = this.GetType().FullName,
|
||||
Success = false,
|
||||
ResultWeight = -1
|
||||
};
|
||||
|
||||
if (fpcParams?.CollateralValuation == null || fpcParams?.ImsAutoFileData == null ||
|
||||
fpcParams?.VehicleFloorPricingParameters == null
|
||||
|| fpcParams?.VehicleFloorPricingParameters?.Any() == false)
|
||||
{
|
||||
// May want to log here, or set a reason code on the result.
|
||||
result.FailureReason = "Missing ImsAuto File, Pricing Parameters, or Collateral Valuation";
|
||||
return result;
|
||||
}
|
||||
|
||||
// 1. use condition to find the correct params.
|
||||
// + VehicleDemicalGradeCode is ignored, ApprovedVehicleGrade is preferred, and Vehicle Grade is the fallback.
|
||||
var condition = fpcParams.ImsAutoFileData.ApprovedVehicleGrade ?? fpcParams.ImsAutoFileData.VehicleGrade;
|
||||
|
||||
if (condition <= 0 || condition == null)
|
||||
{
|
||||
result.FailureReason = "No Condition In AutoIMS File Data";
|
||||
return result;
|
||||
}
|
||||
|
||||
// 2. Get the matching floor pricing calculation row.
|
||||
var floorPricing = Enumerable.First(Enumerable.Where(fpcParams.VehicleFloorPricingParameters,
|
||||
x => condition >= x.LowCondition && condition <= x.HighCondition));
|
||||
|
||||
// 3. Get the floor pricing params based on matching condition.
|
||||
var mmrValue = GetMMRValueByParam(floorPricing.MMRAdjustedValue, fpcParams.ImsAutoFileData,
|
||||
fpcParams.ImsAutoFileData.AuctionGuideBookPrice ?? 0);
|
||||
|
||||
List<Decimal> nadaValues = new List<decimal>();
|
||||
|
||||
nadaValues.Add(GetNadaValueByParam(floorPricing.NADAFirstValue, fpcParams.CollateralValuation));
|
||||
nadaValues.Add(GetNadaValueByParam(floorPricing.NADASecondValue, fpcParams.CollateralValuation));
|
||||
|
||||
bool onlyZeroNadaValues = nadaValues.TrueForAll(x => x == 0);
|
||||
|
||||
if (fpcParams.CollateralValuation.Year <= (DateTime.Now.AddYears(-7).Year) || onlyZeroNadaValues)
|
||||
{
|
||||
nadaValues.Add(fpcParams.CollateralValuation.TradeIn);
|
||||
onlyZeroNadaValues = nadaValues.TrueForAll(x => x == 0);
|
||||
}
|
||||
|
||||
// If there are no NADA or MMR prices, fail the strategy
|
||||
if (onlyZeroNadaValues && (mmrValue == 0 || mmrValue == fpcParams.ImsAutoFileData.AuctionGuideBookPrice))
|
||||
{
|
||||
result.FailureReason = "No NADA or MMR prices returned";
|
||||
return result;
|
||||
}
|
||||
|
||||
// 4. Run the formula
|
||||
var nadaValue = GetNadaAvgValue(nadaValues);
|
||||
|
||||
var divisor = mmrValue == 0 || nadaValue == 0
|
||||
? 1
|
||||
: 2;
|
||||
|
||||
var baseResult = (mmrValue + nadaValue) / divisor;
|
||||
baseResult = (baseResult * floorPricing.PercentageOf) / 100;
|
||||
baseResult = Math.Round(baseResult / 5.0m) * 5;
|
||||
|
||||
result.FloorPrice = baseResult;
|
||||
result.Success = true;
|
||||
result.ResultWeight = GetStrategyWeight();
|
||||
return result;
|
||||
}
|
||||
|
||||
public decimal GetNadaAvgValue(List<Decimal> nadaValues)
|
||||
{
|
||||
Decimal aggregateValue = 0;
|
||||
int valueCount = 0;
|
||||
Decimal aggregateAvgValue = 0;
|
||||
if (nadaValues.Count() > 0)
|
||||
{
|
||||
foreach (var nadaVal in nadaValues)
|
||||
{
|
||||
if (nadaVal > 0)
|
||||
{
|
||||
aggregateValue += nadaVal;
|
||||
valueCount++;
|
||||
}
|
||||
}
|
||||
aggregateAvgValue = aggregateValue / valueCount;
|
||||
}
|
||||
|
||||
return aggregateAvgValue;
|
||||
}
|
||||
|
||||
private decimal GetNadaValueByParam(string floorPricingNadaFirstValue,
|
||||
CollateralValuation fpcParamsCollateralValuation)
|
||||
{
|
||||
if (floorPricingNadaFirstValue.ToLower().StartsWith("low"))
|
||||
{
|
||||
return fpcParamsCollateralValuation.LowAdjustedAuctionValue;
|
||||
}
|
||||
|
||||
if (floorPricingNadaFirstValue.ToLower().StartsWith("avg"))
|
||||
{
|
||||
return fpcParamsCollateralValuation.AverageAdjustedAuctionValue;
|
||||
}
|
||||
|
||||
return fpcParamsCollateralValuation.HighAdjustedAuctionValue;
|
||||
}
|
||||
|
||||
private decimal GetMMRValueByParam(string floorPricingMmrValue, ImsAutoFileData fpcParamsImsAutoFileData,
|
||||
decimal defaultValue)
|
||||
{
|
||||
if (floorPricingMmrValue.ToLower().StartsWith("regional"))
|
||||
{
|
||||
return fpcParamsImsAutoFileData.MMRRegionalAdjustedPrice ?? defaultValue;
|
||||
}
|
||||
|
||||
return fpcParamsImsAutoFileData.MMRNationalAdjustedPrice ?? defaultValue;
|
||||
}
|
||||
|
||||
public int GetStrategyWeight()
|
||||
{
|
||||
return 99; // This is the preferred strategy.
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Services
|
||||
{
|
||||
public interface IRedemptionExpiredDateCalculator
|
||||
{
|
||||
DateTime? CalculateAsync(TemenosCase temenosCase,
|
||||
IReadOnlyCollection<RepoLetter> readOnlyCollection);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.Processing
|
||||
{
|
||||
public class AlertProcessingSlip
|
||||
{
|
||||
public RepoAccountProcessingSlip AccountProcessingSlip { get; set; }
|
||||
public string Message { get; set; }
|
||||
public List<string> WatchedStatusTypes { get; set; }
|
||||
public List<string> WatchedTransactionTypes { get; set; }
|
||||
|
||||
public AlertProcessingSlip
|
||||
(
|
||||
RepoAccountProcessingSlip accountProcessingSlip,
|
||||
string message,
|
||||
List<string> watchedStatusTypes,
|
||||
List<string> watchedTransactionTypes
|
||||
)
|
||||
{
|
||||
AccountProcessingSlip = accountProcessingSlip;
|
||||
Message = message;
|
||||
WatchedStatusTypes = watchedStatusTypes;
|
||||
WatchedTransactionTypes = watchedTransactionTypes;
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Producer;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Settings;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain.Processing
|
||||
{
|
||||
/**
|
||||
* This class represents everything you need to do a logical update or creation of a RepoAccount
|
||||
*/
|
||||
public class RepoAccountProcessingSlip
|
||||
{
|
||||
public ProcessingItem RequestedBy { get; set; }
|
||||
public string AccountNo { get; set; }
|
||||
public int CaseId { get; set; }
|
||||
public TemenosAccount RecoveryAccount { get; set; }
|
||||
public TemenosCase RecoveryCase { get; set; }
|
||||
public RepoAccount ExistingRepoAccount { get; set; }
|
||||
public RepoAccount UpdatedRepoAccount { get; set; }
|
||||
public Settings CurrentSettings { get; set; }
|
||||
|
||||
public IReadOnlyCollection<CoreTransaction> TransactionHistory { get; set; }
|
||||
|
||||
public MemberDetail Member { get; set; }
|
||||
|
||||
public LoanDetail Loan { get; set; }
|
||||
|
||||
public IReadOnlyCollection<AuctionAssignment> AuctionAssignments { get; set; }
|
||||
|
||||
public IReadOnlyCollection<RepoLetter> Letters { get; set; }
|
||||
|
||||
public void AssertReadyForPreChecks()
|
||||
{
|
||||
if (RequestedBy == null)
|
||||
{
|
||||
throw new MissingRequirementException("Processing Item",
|
||||
$"RepoAccount Processing Slip requires a processing item.");
|
||||
}
|
||||
|
||||
if (RecoveryAccount == null)
|
||||
{
|
||||
throw new MissingRequirementException("Recovery Account",
|
||||
$"{RequestedBy.AccountNo} did not have account.");
|
||||
}
|
||||
|
||||
if (CurrentSettings == null)
|
||||
{
|
||||
throw new MissingRequirementException("Settings",
|
||||
$"RepoAccount Processing Slip requires settings.");
|
||||
}
|
||||
}
|
||||
public void AssertReadyForFullProcessing()
|
||||
{
|
||||
AssertReadyForPreChecks();
|
||||
|
||||
if (RecoveryCase == null)
|
||||
{
|
||||
throw new MissingRequirementException("Recovery Case",
|
||||
$"{RecoveryAccount.AccountId} did not have case {CaseId}.");
|
||||
}
|
||||
|
||||
if (Loan == null)
|
||||
{
|
||||
throw new MissingRequirementException("Loan", "Loan required to process.");
|
||||
}
|
||||
|
||||
if (Letters == null)
|
||||
{
|
||||
throw new MissingRequirementException("Letters", "Letters required to process.");
|
||||
}
|
||||
}
|
||||
|
||||
public void AssertReadyForAlertChecks()
|
||||
{
|
||||
AssertReadyForFullProcessing();
|
||||
|
||||
if (TransactionHistory == null && ExistingRepoAccount.ReferenceStatus != "Chargeoff")
|
||||
{
|
||||
throw new MissingRequirementException("Transactions", "Transactions required to process alerts.");
|
||||
}
|
||||
|
||||
if (Member == null)
|
||||
{
|
||||
throw new MissingRequirementException("Member", "Member required to process alerts.");
|
||||
}
|
||||
|
||||
if (AuctionAssignments == null)
|
||||
{
|
||||
throw new MissingRequirementException("Auction Assignments", "Auction Assignments required to process alerts.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.CollectionsApiLib.Models;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Domain
|
||||
{
|
||||
public class RedemptionExpiredDateCalculator : IRedemptionExpiredDateCalculator
|
||||
{
|
||||
public DateTime? CalculateAsync(TemenosCase temenosCase, IReadOnlyCollection<RepoLetter> letters)
|
||||
{
|
||||
try
|
||||
{
|
||||
// !!!!! 04/15/2021, Bill and Kyle don't want to check for letters, they just want to use the date.
|
||||
// var redemptionLettersWi = letters.Where(x => x.ActionCode == "LTR35" || x.ActionCode == "LTR34" ).ToList();
|
||||
// var redemptionLettersIl = letters.Where(x => x.ActionCode == "703" || x.ActionCode == "701").ToList();
|
||||
|
||||
// if (redemptionLettersIl.Any() && redemptionLettersWi.Any())
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
if (temenosCase.RedemptionExpiredDateWi != null) { // redemptionLettersWi.Any())}
|
||||
return temenosCase.RedemptionExpiredDateWi;
|
||||
}
|
||||
|
||||
if( temenosCase.RedemptionExpiredDateIl != null) { // redemptionLettersIl.Any())}
|
||||
return temenosCase.RedemptionExpiredDateIl;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed To Get Redemption Exp. Date. TemenosCaseID: {temenosCase.CaseId}", temenosCase);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Exceptions
|
||||
{
|
||||
public class AccountCreationFailed : ApplicationException
|
||||
{
|
||||
public AccountCreationFailed(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a missing requirement for processing an account; this account CANNOT be tried again later.
|
||||
/// </summary>
|
||||
public class IneligibleAccountException : Exception
|
||||
{
|
||||
public string Field { get; set; }
|
||||
public string Reason { get; set; }
|
||||
|
||||
public IneligibleAccountException(string field, string reason) : base(field)
|
||||
{
|
||||
Field = field;
|
||||
Reason = reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a missing requirement for processing an account; which will mean the account can be tried again later.
|
||||
/// </summary>
|
||||
public class MissingRequirementException : Exception
|
||||
{
|
||||
public string Field { get; set; }
|
||||
public string Advice { get; set; }
|
||||
|
||||
public MissingRequirementException(string field, string advice) : base(field)
|
||||
{
|
||||
Field = field;
|
||||
Advice = advice;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Exceptions
|
||||
{
|
||||
/**
|
||||
* If an entity cannot be found, that specific error can allow for a 404 or 400 with more detail to be
|
||||
* returned. So specify that to the API layer.
|
||||
*/
|
||||
public class NotFoundException : ApplicationException
|
||||
{
|
||||
public NotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Exceptions
|
||||
{
|
||||
/**
|
||||
* Do not want to expose database and related exceptions to the controllers / web layer.
|
||||
* The lower layers log what happened for developers, then return a general error; unless
|
||||
* a more specific error can be returned in a way to allow the client to act (such as NotFound).
|
||||
*/
|
||||
// IF YOU SEE THIS EXCEPTION IN SEQ, IT MEANS YES, YOU HAVE TO INVESTIGATE.
|
||||
public class ServerErrorException : Exception
|
||||
{
|
||||
public ServerErrorException(): base("[INVESTIGATE] A server error occured. See associated logs.") { }
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Extensions
|
||||
{
|
||||
public static class ModelStateMessages
|
||||
{
|
||||
public static List<string> GetErrorMessages(this ModelStateDictionary dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
return dictionary.SelectMany(m => m.Value.Errors)
|
||||
.Select(m => m.ErrorMessage)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using LCU.API.Collections.RepoApi.HostedServices;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.API.Collections.RepoApi.Services.AutoIms;
|
||||
using LCU.API.Collections.RepoApi.Services.Producer;
|
||||
using LCU.API.Collections.RepoApi.Services.Reporting;
|
||||
using LCU.Shared.Collections.RepoApiLib.Repositories;
|
||||
using LCU.Shared.Collections.RepoApiLib.Repositories.Settings;
|
||||
using LCU.Shared.Collections.RepoApiLib.WebServices.NADA;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Extensions
|
||||
{
|
||||
public static class RepoApiStartup
|
||||
{
|
||||
public static void UseRepoServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IAccountAlertCheckService, AccountAlertCheckService>();
|
||||
services.AddScoped<IAuctionAssignmentService, AuctionAssignmentService>();
|
||||
services.AddScoped<IAuctionFtpClientService, AuctionFtpClientService>();
|
||||
services.AddScoped<IAuctionHouseService, AuctionHouseService>();
|
||||
services.AddScoped<IAuctionReportingService, AuctionReportingService>();
|
||||
services.AddScoped<IAuditRecordService, AuditRecordService>();
|
||||
services.AddScoped<IAutoImsAuctionProcessorService, AutoImsAuctionProcessorService>();
|
||||
services.AddScoped<IAutoImsDownloaderService, AutoImsDownloaderService>();
|
||||
services.AddScoped<IAutoImsImporterService, AutoImsImporterService>();
|
||||
services.AddScoped<IAutoImsNoteProcessorService, AutoImsNoteProcessorService>();
|
||||
services.AddScoped<ICollateralService, CollateralService>();
|
||||
services.AddScoped<ICollateralValueService, CollateralValueService>();
|
||||
services.AddScoped<IRepoAccountProcessorService, RepoAccountProcessorService>();
|
||||
services.AddScoped<IEventAlertService, EventAlertService>();
|
||||
services.AddScoped<IFactoidService, FactoidService>();
|
||||
services.AddScoped<IFloorPricingService, FloorPricingService>();
|
||||
services.AddScoped<IImsAutoFileDataConverter, ImsAutoFileDataConverter>();
|
||||
services.AddScoped<IImsAutoFileDataService, ImsAutoFileDataService>();
|
||||
services.AddScoped<IImsAutoFileNoteConverter, ImsAutoFileNoteConverter>();
|
||||
services.AddScoped<ILandmarkReportingService, LandmarkReportingService>();
|
||||
services.AddScoped<IRefundStatusService, RefundStatusService>();
|
||||
services.AddScoped<IRepoAccountService, RepoAccountService>();
|
||||
services.AddScoped<IRepoCompanyService, RepoCompanyService>();
|
||||
services.AddScoped<IRepoCompanySelectionService, RepoCompanySelectionService>();
|
||||
services.AddScoped<IRepoCompanyReportingService, RepoCompanyReportingService>();
|
||||
services.AddScoped<IRepoLetterService, RepoLetterService>();
|
||||
services.AddScoped<IRepoQueueProducerService, RepoQueueProducerService>();
|
||||
services.AddScoped<IRepoStatusChangeService, RepoStatusChangeService>();
|
||||
services.AddScoped<ISavedLocationService, SavedLocationService>();
|
||||
services.AddScoped<ISettingsService, SettingsService>();
|
||||
services.AddScoped<IVehicleFloorPricingParametersService, VehicleFloorPricingParametersService>();
|
||||
services.AddScoped<IProcessingReceiptService, ProcessingReceiptService>();
|
||||
}
|
||||
|
||||
public static void UseRepoRepositories(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IAuditRecordRepository, AuditRecordRepository>();
|
||||
services.AddScoped<IAccountTypeRepository, AccountTypeRepository>();
|
||||
services.AddScoped<IAllowedStatusChangeRepository, AllowedStatusChangeRepository>();
|
||||
services.AddScoped<IAuctionAssignmentRepository, AuctionAssignmentRepository>();
|
||||
services.AddScoped<IAuctionAssignmentNoteRepository, AuctionAssignmentNoteRepository>();
|
||||
services.AddScoped<IBodyStyleRepository, BodyStyleRepository>();
|
||||
services.AddScoped<ICleaningLevelRepository, CleaningLevelRepository>();
|
||||
services.AddScoped<ICollateralRepository, CollateralRepository>();
|
||||
services.AddScoped<ICollateralTypeRepository, CollateralTypeRepository>();
|
||||
services.AddScoped<ICollateralTypeToVehicleTypeRepository, CollateralTypeToVehicleTypeRepository>();
|
||||
services.AddScoped<IEventAlertRepository, EventAlertRepository>();
|
||||
services.AddScoped<IHoldStatusRepository, HoldStatusRepository>();
|
||||
services.AddScoped<IImsAutoFileDataRepository, ImsAutoFileDataRepository>();
|
||||
services.AddScoped<ILetterRepository, LetterRepository>();
|
||||
services.AddScoped<ILoanCategoryRepository, LoanCategoryRepository>();
|
||||
services.AddScoped<ILocationTypeRepository, LocationTypeRepository>();
|
||||
services.AddScoped<IRepoStatusChangeRepository, RepoStatusChangeRepository>();
|
||||
services.AddScoped<IRepoAccountRepository, RepoAccountRepository>();
|
||||
services.AddScoped<IRepoCompanyRepository, RepoCompanyRepository>();
|
||||
services.AddScoped<IVehicleTypeRepository, VehicleTypeRepository>();
|
||||
services.AddScoped<IVehicleFloorPricingParametersRepository, VehicleFloorPricingParametersRepository>();
|
||||
services.AddScoped<IAuctionHouseRepository, AuctionHouseRepository>();
|
||||
services.AddScoped<IReportingRepository, ReportingRepository>();
|
||||
services.AddScoped<IRefundStatusRepository, RefundStatusRepository>();
|
||||
services.AddScoped<IRepoCompanySelectionRepository, RepoCompanySelectionRepository>();
|
||||
services.AddScoped<IRepoManualStatusRepository, RepoManualStatusRepository>();
|
||||
services.AddScoped<ISavedLocationRepository, SavedLocationRepository>();
|
||||
services.AddScoped<IProcessingReceiptRepository, ProcessingReceiptRepository>();
|
||||
services.AddScoped<IExternalUserRepository, ExternalUserRepository>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using LCU.Shared.Collections.RepoApiLib.Formatting;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting.Responses;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Factories
|
||||
{
|
||||
public enum ReportSumType { WholeNumber, Currency, WholeNumberAvg, CurrencyAvg, PercentageAvg }
|
||||
|
||||
public static class ReportTableFactory
|
||||
{
|
||||
|
||||
public static ReportTable CreateHistoricalSummationTable<T>(
|
||||
ReportSumType reportType,
|
||||
IEnumerable<T> dataRows,
|
||||
string pluralEntityName,
|
||||
DateTime lowDateTime,
|
||||
DateTime highDateTime,
|
||||
Func<T, decimal?> cellValue,
|
||||
Func<T, string> uniqueRowDisplay,
|
||||
Func<T, DateTime?> dateTimeField,
|
||||
Func<T, object> rowKey,
|
||||
bool showAbsenceRow = true
|
||||
)
|
||||
{
|
||||
var totalMonths = CalculateTotalMonths(lowDateTime, highDateTime);
|
||||
var rows = CreateUniqueRowNames(dataRows, uniqueRowDisplay, pluralEntityName, showAbsenceRow);
|
||||
|
||||
var reportTable = new ReportTable()
|
||||
{
|
||||
Headers = CreateMonthHeaders(lowDateTime, highDateTime, new string[] { "" }, new string[] { "Total" }),
|
||||
Cells = new ReportCell[rows.Count(), totalMonths + 2]
|
||||
};
|
||||
|
||||
// Build the Report Table Values
|
||||
var uniqueRowIdentifiers = new List<object>();
|
||||
var rawValueTableLengths = new int[rows.Count(), totalMonths + 1];
|
||||
var rawValueTable = new decimal[rows.Count(), totalMonths + 1];
|
||||
foreach (var currentDataRow in dataRows)
|
||||
{
|
||||
var rowDisplay = uniqueRowDisplay(currentDataRow);
|
||||
var currentKey = rowKey(currentDataRow);
|
||||
var currentValue = cellValue(currentDataRow);
|
||||
var currentDateOfRow = dateTimeField(currentDataRow);
|
||||
|
||||
if (currentDateOfRow != null &&
|
||||
currentValue != null &&
|
||||
!uniqueRowIdentifiers.Contains(currentKey))
|
||||
{
|
||||
uniqueRowIdentifiers.Add(currentKey);
|
||||
|
||||
var monthIndex = CalculateMonthOffset(lowDateTime, currentDateOfRow.Value);
|
||||
var currentItemIndex = FindUniqueRowIndex(rows, rowDisplay);
|
||||
|
||||
// "All Entities" Value
|
||||
rawValueTable[0, monthIndex] += currentValue.Value; // Totals for Month
|
||||
rawValueTable[0, totalMonths] += currentValue.Value; // Totals for All
|
||||
|
||||
// "All Entities" Length
|
||||
rawValueTableLengths[0, monthIndex] += 1; // Totals for Month
|
||||
rawValueTableLengths[0, totalMonths] += 1; // Totals for All
|
||||
|
||||
// Current Auction Row
|
||||
if (currentItemIndex != 0)
|
||||
{
|
||||
rawValueTable[currentItemIndex, monthIndex] += currentValue.Value; // Total for Month
|
||||
rawValueTable[currentItemIndex, totalMonths] += currentValue.Value; // Total for Auction House.
|
||||
|
||||
rawValueTableLengths[currentItemIndex, monthIndex] += 1; // Total for Month
|
||||
rawValueTableLengths[currentItemIndex, totalMonths] += 1; // Total for Auction House.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Report Table into ReportCells
|
||||
for (var i = 0; i < rawValueTable.GetLength(0); i++)
|
||||
{
|
||||
for (var j = 0; j < rawValueTable.GetLength(1); j++)
|
||||
{
|
||||
var rawValue = rawValueTable[i, j];
|
||||
var columnIndex = j + 1; // Add 1 to compensate for Row Display Column
|
||||
|
||||
if (reportType == ReportSumType.WholeNumberAvg ||
|
||||
reportType == ReportSumType.PercentageAvg ||
|
||||
reportType == ReportSumType.CurrencyAvg)
|
||||
{
|
||||
var rawValueLength = rawValueTableLengths[i, j];
|
||||
if (rawValueLength != 0)
|
||||
{
|
||||
rawValue = rawValue / rawValueLength;
|
||||
}
|
||||
}
|
||||
|
||||
switch(reportType)
|
||||
{
|
||||
case ReportSumType.WholeNumberAvg:
|
||||
case ReportSumType.WholeNumber:
|
||||
reportTable.Cells[i, columnIndex] = ReportCellFormatting.CreateIntCell(Convert.ToInt32(rawValue));
|
||||
break;
|
||||
case ReportSumType.Currency:
|
||||
case ReportSumType.CurrencyAvg:
|
||||
reportTable.Cells[i, columnIndex] = ReportCellFormatting.CreateCurrencyCell(rawValue);
|
||||
break;
|
||||
case ReportSumType.PercentageAvg:
|
||||
reportTable.Cells[i, columnIndex] = ReportCellFormatting.CreatePercentageCell(rawValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add Auction Names to Left Column
|
||||
for (var i = 0; i < rows.Count(); i++)
|
||||
{
|
||||
reportTable.Cells[i, 0] = ReportCellFormatting.CreateStringCell(rows.ElementAt(i));
|
||||
}
|
||||
|
||||
return reportTable;
|
||||
}
|
||||
|
||||
public static int FindUniqueRowIndex(IEnumerable<string> uniqueRows, string columnName)
|
||||
{
|
||||
if (columnName != null)
|
||||
{
|
||||
foreach (var item in uniqueRows.Select((value, index) => new { index, value }))
|
||||
{
|
||||
if (item.value == columnName)
|
||||
{
|
||||
return item.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueRows.Count() - 1;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> CreateUniqueRowNames<T>(IEnumerable<T> reportData, Func<T, string> uniqueRowDisplay, string pluralEntityName, bool showAbsenceRow = true)
|
||||
{
|
||||
var list = new List<string>();
|
||||
list.Add("All " + pluralEntityName);
|
||||
|
||||
if (uniqueRowDisplay != null)
|
||||
{
|
||||
foreach (var row in reportData)
|
||||
{
|
||||
var rowName = uniqueRowDisplay(row);
|
||||
if (rowName != null)
|
||||
{
|
||||
if (!list.Contains(rowName))
|
||||
{
|
||||
list.Add(rowName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showAbsenceRow)
|
||||
{
|
||||
list.Add("No " + pluralEntityName);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static string[] CreateMonthHeaders(DateTime lowDateTime, DateTime highDateTime, string[] preHeaders = null, string[] postHeaders = null)
|
||||
{
|
||||
var totalMonths = CalculateTotalMonths(lowDateTime, highDateTime);
|
||||
var preHeadersLength = preHeaders?.Length ?? 0;
|
||||
var postHeadersLength = postHeaders?.Length ?? 0;
|
||||
string[] monthHeaders = new string[totalMonths + preHeadersLength + postHeadersLength];
|
||||
|
||||
for (var i = 0; i < preHeadersLength; i++)
|
||||
{
|
||||
monthHeaders[i] = preHeaders[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < totalMonths; i++)
|
||||
{
|
||||
var currentDate = lowDateTime.AddMonths(i);
|
||||
monthHeaders[preHeadersLength + i] = $"{currentDate.ToString("MMM", CultureInfo.InvariantCulture)} {currentDate.Year}";
|
||||
}
|
||||
|
||||
for (var i = 0; i < postHeadersLength; i++)
|
||||
{
|
||||
monthHeaders[preHeadersLength + totalMonths + i] = postHeaders[i];
|
||||
}
|
||||
|
||||
return monthHeaders;
|
||||
}
|
||||
|
||||
private static int CalculateTotalMonths(DateTime lowDateTime, DateTime highDateTime)
|
||||
{
|
||||
return CalculateMonthOffset(lowDateTime, highDateTime) + 1;
|
||||
}
|
||||
|
||||
private static int CalculateMonthOffset(DateTime lowDateTime, DateTime highDateTime)
|
||||
{
|
||||
return (((highDateTime.Year - lowDateTime.Year) * 12) + highDateTime.Month - lowDateTime.Month);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LCU.API.Collections.RepoApi.Models.Reporting;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting.Responses;
|
||||
|
||||
namespace LCU.Shared.Collections.RepoApiLib.Formatting
|
||||
{
|
||||
public static class DataTableFormatting
|
||||
{
|
||||
|
||||
public static DataTable CreateDataTable<T>(IEnumerable<T> dataRows, Func<T, int> repoID, params Func<T, ReportHeaderCellMap>[] columns)
|
||||
{
|
||||
return new DataTable()
|
||||
{
|
||||
Headers = columns.Select(column => column(default(T)).Header).ToArray(),
|
||||
Rows = dataRows.Select(entry => new RepoAccountReferenceRow()
|
||||
{
|
||||
RepoId = repoID(entry),
|
||||
Cells = columns.Select(column => column(entry).Cell).ToArray()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting.Responses;
|
||||
|
||||
namespace LCU.Shared.Collections.RepoApiLib.Formatting
|
||||
{
|
||||
public static class ReportCellFormatting
|
||||
{
|
||||
public static ReportCell CreateStringCell(string display)
|
||||
{
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = string.IsNullOrEmpty(display) ? "-" : display
|
||||
};
|
||||
}
|
||||
|
||||
public static ReportCell CreateIntCell(int? number)
|
||||
{
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = (number == null || number == 0) ? "-" : number?.ToString(),
|
||||
Value = number
|
||||
};
|
||||
}
|
||||
|
||||
public static ReportCell CreateDecimalCell(decimal? number, bool displayZeroValue = false)
|
||||
{
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = (number == null || (number == 0 && !displayZeroValue)) ? "-" : number?.ToString(),
|
||||
Value = number
|
||||
};
|
||||
}
|
||||
|
||||
public static ReportCell CreatePercentageCell(decimal? number, bool isNormalized = false)
|
||||
{
|
||||
var display = "-";
|
||||
if (number != null && number != 0)
|
||||
{
|
||||
if (!isNormalized)
|
||||
{
|
||||
number = number / 100;
|
||||
}
|
||||
display = string.Format(System.Globalization.CultureInfo.InvariantCulture,"{0:P2}", number);
|
||||
}
|
||||
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = display,
|
||||
Value = number
|
||||
};
|
||||
}
|
||||
|
||||
public static ReportCell CreateDateCell(DateTime? dateTime)
|
||||
{
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = dateTime?.ToString("MM/dd/yyyy") ?? "-",
|
||||
Value = dateTime
|
||||
};
|
||||
}
|
||||
|
||||
public static ReportCell CreateCurrencyCell(decimal? amount)
|
||||
{
|
||||
return new ReportCell()
|
||||
{
|
||||
Display = (amount == null || amount == 0) ? "-" : string.Format("{0:C}", amount),
|
||||
Value = amount
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
using System;
|
||||
using LCU.API.Collections.RepoApi.Models.Reporting;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting;
|
||||
|
||||
namespace LCU.Shared.Collections.RepoApiLib.Formatting
|
||||
{
|
||||
public static class StdReportEntryColumns
|
||||
{
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Member(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Member",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.IndividualName)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> AccountNo(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Account No",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.AccountNo)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> AccountCode(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Account Code",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.AccountTypeCode)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Balance(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Balance",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.Balance)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Status(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Status",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.Status)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DaysInStatus(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Days in Status",
|
||||
Cell = ReportCellFormatting.CreateIntCell(entry?.DaysInStatus)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Bankruptcy(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Bankruptcy",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.Bankruptcy)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Collector(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Collector",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.Collector)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DaysCurrentDelq(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Days Current Delq",
|
||||
Cell = ReportCellFormatting.CreateIntCell(entry?.DaysCurrentDelq)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DaysDelqWhenAdded(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Days Delq When Added",
|
||||
Cell = ReportCellFormatting.CreateIntCell(entry?.DaysDelqWhenAdded)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DelinquentAmount(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Delinquent Amount",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.DelinquentAmount)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> LastPaymentAmount(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Last Payment Amount",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.LastPaymentAmount)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> LastPaymentDate(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Last Payment Date",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.LastPaymentDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Auction(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Auction",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.AuctionName)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Collateral(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Collateral",
|
||||
Cell = ReportCellFormatting.CreateStringCell($"{entry?.Year} {entry?.Make} {entry?.Model}")
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> RepoCompany(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Repo Company",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.RepoCompany)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> RepoType(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Repo Type",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.RepoType)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DateAssigned(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Date Assigned",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.RepoCompanyAssignmentDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DateRepoed(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Date Repoed",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.DateRepoed)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> RepoDuration(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Repo Duration",
|
||||
Cell = ReportCellFormatting.CreateIntCell(entry?.RepoDuration)
|
||||
};
|
||||
}
|
||||
|
||||
// IMSAutoFile Data
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> AutoImsStatus(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "AutoIMS Status",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.AutoImsStatus)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DateSold(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Date Sold",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.SoldDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> DateEnteredAutoIMS(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Date Entered AutoIMS",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.DateEnteredAutoIMS)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> ScheduledSaleDate(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Scheduled Sale Date",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.ScheduledSaleDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> GrossPercentFloor(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Gross % Floor",
|
||||
Cell = ReportCellFormatting.CreatePercentageCell(entry?.GrossPercentFloor)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> NetPercentFloor(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Net % Floor",
|
||||
Cell = ReportCellFormatting.CreatePercentageCell(entry?.NetPercentFloor)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> TotalCharges(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Total Charges",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.TotalCharges)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> Grade(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Grade",
|
||||
Cell = ReportCellFormatting.CreateDecimalCell(entry?.Grade, true)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> FloorPrice(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Floor Price",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.FloorPrice)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> GrossSalePrice(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Gross Sale Price",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.GrossSalePrice)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> FilteredStatus(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Filtered Status",
|
||||
Cell = ReportCellFormatting.CreateStringCell(entry?.FilteredStatus)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> FilteredStatusChangeDate(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Filtered Status Changed On",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.FilteredStatusChangeDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> O4RDate(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "First O4R",
|
||||
Cell = ReportCellFormatting.CreateDateCell(entry?.O4RDate)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> SurplusDeficiency(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Surplus / Deficiency",
|
||||
Cell = ReportCellFormatting.CreateCurrencyCell(entry?.GrossSalePrice - entry?.Balance)
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<StdReportEntry, ReportHeaderCellMap> AutoIMSAssignmentToSold(string alternateHeader = null)
|
||||
{
|
||||
return (StdReportEntry entry) => new ReportHeaderCellMap()
|
||||
{
|
||||
Header = alternateHeader ?? "Days AutoIMS to Sold",
|
||||
Cell = ReportCellFormatting.CreateIntCell(
|
||||
entry != null ?
|
||||
(
|
||||
(entry.DateEnteredAutoIMS != null && entry.SoldDate != null) ?
|
||||
(int?) (entry.SoldDate.Value - entry.DateEnteredAutoIMS.Value).TotalDays
|
||||
: null
|
||||
)
|
||||
: null
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Realtime.QueueLib.Models;
|
||||
using LCU.Shared.Realtime.QueueLib.Queue;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.HostedServices
|
||||
{
|
||||
public class HostedAutoImsTaskConsumer : BackgroundService, IQueueItemProcessor
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly IQueueConnectionConfig _configuration;
|
||||
private readonly IQueueConsumerConfig _consumerConfig;
|
||||
|
||||
public HostedAutoImsTaskConsumer(IQueueConnectionConfig configuration, RepoApiConfiguration repoApiConfiguration, IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_scopeFactory = scopeFactory;
|
||||
_consumerConfig = repoApiConfiguration.QueueConsumerConfigs["AutoImsTask"];
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Information("[QueueLib] Hosted Queue Producer is starting.");
|
||||
|
||||
var proxyQueue = new QueueItemConsumer(_configuration, _consumerConfig, this);
|
||||
|
||||
await proxyQueue.ProcessItems(cancellationToken, true, 0);
|
||||
|
||||
Log.Information("[QueueLib] Hosted Queue Producer is stopping.");
|
||||
}
|
||||
|
||||
public void HandleItem(QueueItem item)
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var processor = scope.ServiceProvider.GetService<AutoImsService>();
|
||||
var files = processor.Download();
|
||||
processor.ProcessDownloadedFiles(files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.Shared.Collections.RepoApiLib.Hosted;
|
||||
using LCU.Shared.Realtime.QueueLib.Models;
|
||||
using LCU.Shared.Realtime.QueueLib.Queue;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.HostedServices
|
||||
{
|
||||
public class HostedRepoTaskConsumer : BackgroundService, IQueueItemProcessor
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly IQueueConnectionConfig _configuration;
|
||||
private readonly IQueueConsumerConfig _consumerConfig;
|
||||
|
||||
public HostedRepoTaskConsumer(IQueueConnectionConfig configuration, RepoApiConfiguration repoApiConfiguration, IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_scopeFactory = scopeFactory;
|
||||
_consumerConfig = repoApiConfiguration.QueueConsumerConfigs["RepoProcessorTask"];
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Information("Hosted Queue Producer is starting.");
|
||||
|
||||
try
|
||||
{
|
||||
var proxyQueue = new QueueItemConsumer(_configuration, _consumerConfig, this);
|
||||
|
||||
await proxyQueue.ProcessItems(cancellationToken, true, 0);
|
||||
}
|
||||
catch (TaskCanceledException ex) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Log.Information(ex, "Repo Producer was shut down normally.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error in main thread for HostedRepoTaskConsumer");
|
||||
}
|
||||
|
||||
Log.Information("Hosted Repo Producer is stopping.");
|
||||
}
|
||||
|
||||
public void HandleItem(QueueItem item)
|
||||
{
|
||||
var settings = new TaskSettings();
|
||||
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var queueProducerService = scope.ServiceProvider.GetRequiredService<IRepoQueueProducerService>();
|
||||
queueProducerService.ImportData(settings).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Shared.Collections.RepoApiLib.Hosted;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Producer;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.HostedServices
|
||||
{
|
||||
public interface IRepoQueueProducerService
|
||||
{
|
||||
Task ImportData(TaskSettings settings);
|
||||
void SendRequestToQueue(ProcessingItem queueRequest);
|
||||
}
|
||||
}
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.Exceptions;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.API.Collections.RepoApi.Services.Producer;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Consumer;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Producer;
|
||||
using LCU.Shared.Collections.RepoApiLib.Repositories;
|
||||
using LCU.Shared.Realtime.QueueLib.Models;
|
||||
using LCU.Shared.Realtime.QueueLib.Queue;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.HostedServices
|
||||
{
|
||||
/**
|
||||
* Special Notes.
|
||||
* 1. Direct injection of services cannot be done, the main API context is on it's own
|
||||
* scope/thread. So instead, the scope factory is passed, so that this can create scopes.
|
||||
* 2. This is designed to use 5 threads, from within an IHostedService thread context, to
|
||||
* process queues. It's designed to do this in 'batches' to simulate a batch job, which
|
||||
* is where the creation of the RunInfo comes into scope.
|
||||
* 3. There is a main 'thread' here, which waits for a 'batch' of work to be complete.
|
||||
* 4. Under the main thread, there are 5 worker threads, communicating using two queues besides
|
||||
* the main Rabbit queue. The queues are used to set something up for processing (_inputQueue)
|
||||
* and to mark something as done processing (_outputQueue).
|
||||
*/
|
||||
public class RepoQueueConsumerService : BackgroundService, IQueueItemProcessor
|
||||
{
|
||||
private readonly IQueueConnectionConfig _connectionConfig;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private RepoRunInfo _runInfo;
|
||||
|
||||
private const int ThreadCount = 5;
|
||||
private BlockingCollection<QueueItem> _inputQueue;
|
||||
private BlockingCollection<QueueResult> _outputQueue;
|
||||
private readonly ConcurrentDictionary<string, ProcessingItem> _inProgress;
|
||||
private readonly Task[] _workers;
|
||||
private Task _consumer;
|
||||
private QueueItemConsumer _queue;
|
||||
private readonly IQueueConsumerConfig _consumerConfig;
|
||||
private CancellationTokenSource _individualRunToken;
|
||||
|
||||
public RepoQueueConsumerService(IQueueConnectionConfig connectionConfig, RepoApiConfiguration repoApiConfiguration, IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_connectionConfig = connectionConfig;
|
||||
_scopeFactory = scopeFactory;
|
||||
_inProgress = new ConcurrentDictionary<string, ProcessingItem>();
|
||||
_workers = new Task[ThreadCount];
|
||||
_consumerConfig = repoApiConfiguration.QueueConsumerConfigs["RepoAccountQueue"];
|
||||
}
|
||||
|
||||
bool TopLevelThreadController()
|
||||
{
|
||||
Log.Information($"[Startup] {_consumerConfig.GetQueueName()} Consumer is starting a run.");
|
||||
_runInfo = new RepoRunInfo();
|
||||
|
||||
foreach (var workItem in _outputQueue.GetConsumingEnumerable())
|
||||
{
|
||||
_queue.AckItem(workItem.DeliveryTag);
|
||||
if (workItem.Receipt != null)
|
||||
{
|
||||
_runInfo.Receipts.Add(workItem.Receipt);
|
||||
}
|
||||
|
||||
var remainingCount = _queue.CheckQueueCount();
|
||||
// First the input queue will finish, and we have to let running tasks finish.
|
||||
if (remainingCount == 0 && EnumerableExtensions.Any(_inputQueue) == false && !_inputQueue.IsCompleted)
|
||||
{
|
||||
_inputQueue.CompleteAdding();
|
||||
Task.WaitAll(_workers);
|
||||
}
|
||||
|
||||
// That will cause the output queue to have the last THREADCOUNT items added, but
|
||||
// we are IN the loop that processes them, so wen can't wait, just continue...
|
||||
if (remainingCount == 0 && _inputQueue.IsCompleted && !_outputQueue.IsAddingCompleted)
|
||||
{
|
||||
_outputQueue.CompleteAdding();
|
||||
}
|
||||
|
||||
// Ok, both queues are complete, which means all done.
|
||||
if (_inputQueue.IsCompleted && _outputQueue.IsCompleted)
|
||||
{
|
||||
_individualRunToken.Cancel();
|
||||
_inputQueue.Dispose();
|
||||
_outputQueue.Dispose();
|
||||
Log.Information($"{_consumerConfig.GetQueueName()} run completed... recording RunInfo: {@_runInfo}", _runInfo);
|
||||
_runInfo.CompleteDateTime = DateTime.Now;
|
||||
SaveRunInfo(_runInfo);
|
||||
SendErrorNotification(_runInfo);
|
||||
SendAlertNotification(_runInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Error($"[Startup] {_consumerConfig.GetQueueName()} Consumer is finished in an unexpected way. {@_runInfo}", _runInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SaveRunInfo(RepoRunInfo runInfo)
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
var receiptService = scope.ServiceProvider.GetRequiredService<IProcessingReceiptService>();
|
||||
receiptService.SaveAllAsync(runInfo.Receipts).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Could not save repo processor results.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendErrorNotification(RepoRunInfo runInfo)
|
||||
{
|
||||
if (runInfo.Receipts.Count > 0)
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var resp = scope.ServiceProvider.GetService<IEmailer>().SendProccessSummary(_runInfo);
|
||||
if (!resp.Result.IsSuccessStatusCode)
|
||||
{
|
||||
Log.Error(
|
||||
$"RepoQueueConsumerService failed to send error email. Emailer Response: {resp.Result.StatusCode} - {resp.Result.ReasonPhrase}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendAlertNotification(RepoRunInfo runInfo)
|
||||
{
|
||||
if (runInfo.Receipts.Count > 0)
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var resp = scope.ServiceProvider.GetService<IEmailer>().SendAlertSummaryEmailWithEndTime(runInfo.CompleteDateTime?? DateTime.Now);
|
||||
if (!resp.Result.IsSuccessStatusCode)
|
||||
{
|
||||
Log.Error(
|
||||
$"RepoQueueConsumerService failed to send alerts email. Emailer Response: {resp.Result.StatusCode} - {resp.Result.ReasonPhrase}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SingleThreadMessageWorker(int workerId)
|
||||
{
|
||||
Log.Debug("[Startup] Worker {0} is starting.", workerId);
|
||||
foreach (var workItem in _inputQueue.GetConsumingEnumerable())
|
||||
{
|
||||
var message = Encoding.UTF8.GetString(workItem.Body.ToArray());
|
||||
Log.Information("Worker {0} Processing {1}", workerId, message);
|
||||
var result = HandleSingleItem(message).GetAwaiter().GetResult();
|
||||
result.DeliveryTag = workItem.DeliveryTag;
|
||||
_outputQueue.Add(result);
|
||||
}
|
||||
|
||||
Log.Debug("[Shutdown] Worker {0} is complete.", workerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the main function run by the IHostedService interface, so
|
||||
* should be considered the 'main' here.
|
||||
*/
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (_queue == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_queue = new QueueItemConsumer(_connectionConfig, _consumerConfig, this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to create RepoQueueConsumerService, Will try again in 1 minute.");
|
||||
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
Log.Debug($"{_consumerConfig.GetQueueName()} Processor started a run.");
|
||||
_inputQueue = new BlockingCollection<QueueItem>();
|
||||
_outputQueue = new BlockingCollection<QueueResult>();
|
||||
_consumer = Task<bool>.Factory.StartNew(TopLevelThreadController, stoppingToken);
|
||||
for (var threadIndex = 0; threadIndex < ThreadCount; ++threadIndex)
|
||||
{
|
||||
var workerId = threadIndex;
|
||||
var task = new Task(() => SingleThreadMessageWorker(workerId));
|
||||
_workers[threadIndex] = task;
|
||||
task.Start();
|
||||
}
|
||||
|
||||
_individualRunToken = new CancellationTokenSource();
|
||||
await _queue.ProcessItems(_individualRunToken.Token, false, ThreadCount);
|
||||
|
||||
while (!_consumer.IsCompleted)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
Log.Debug($"{_consumerConfig.GetQueueName()} Finishing Heartbeat.");
|
||||
}
|
||||
|
||||
_consumer?.Dispose();
|
||||
_individualRunToken.Dispose();
|
||||
Log.Debug($"{_consumerConfig.GetQueueName()} Processor Completed a run.");
|
||||
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||
}
|
||||
|
||||
// the thread is cancelled, so the loop will exit, be sure to propagate the cancellation to our token
|
||||
if (_individualRunToken != null && _individualRunToken.IsCancellationRequested == false)
|
||||
{
|
||||
_individualRunToken.Cancel();
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException ex) when (stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
Log.Information(ex, "Repo Processor was shut down normally.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||
.Fatal(ex,$"There is an unhandled exception when processing repos; The IIS App Pool may need to be restarted.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<QueueResult> HandleSingleItem(string itemAsString)
|
||||
{
|
||||
var result = new QueueResult();
|
||||
ProcessingItem item = null;
|
||||
try
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var repoAccountProcessor = scope.ServiceProvider.GetRequiredService<IRepoAccountProcessor>();
|
||||
|
||||
item = JsonConvert.DeserializeObject<ProcessingItem>(itemAsString);
|
||||
Log.Debug($"{_consumerConfig.GetQueueName()} Consumer received: {@item}", item);
|
||||
|
||||
var newKey = item.DetermineAuditItemKey();
|
||||
if (_inProgress.TryAdd(newKey, item) == false)
|
||||
{
|
||||
Log.Information("Conflicting item found for {0}, skipping.",
|
||||
item.DetermineAuditItemKey(),
|
||||
_inProgress[newKey].DetermineAuditItemKey());
|
||||
return result;
|
||||
}
|
||||
|
||||
var receipt = new ProcessingReceipt(item.AccountNo)
|
||||
{
|
||||
AccountId = item.AccountId ?? "",
|
||||
CaseId = item.CaseId ?? "",
|
||||
QueuedBy = item.QueuedBy
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information($"Processing {item.AccountNo}");
|
||||
receipt.Outcome = await repoAccountProcessor.HandleAccount(item);
|
||||
receipt.Message = "Success";
|
||||
}
|
||||
catch (IneligibleAccountException iaEx)
|
||||
{
|
||||
receipt.Message = $"The item {item.AccountNo} is ineligible {iaEx.Field} : {iaEx.Reason}";
|
||||
receipt.Outcome = RepoProcessingResult.Ineligible;
|
||||
}
|
||||
catch (MissingRequirementException mrEx)
|
||||
{
|
||||
receipt.Message = $"The item {item.AccountNo} is missing {mrEx.Field} : {mrEx.Advice}.";
|
||||
receipt.Outcome = RepoProcessingResult.MissingRequirement;
|
||||
}
|
||||
catch (Exception ex) /* Mostly ServerErrorException */
|
||||
{
|
||||
receipt.Message = $"The item {item.AccountNo} produced an error. Please investigate.";
|
||||
Log.Error(ex, receipt.Message);
|
||||
}
|
||||
|
||||
if (!_inProgress.TryRemove(newKey, out item))
|
||||
{
|
||||
Log.Error("Couldn't remove item from concurrent dictionary. (Success branch) {@item}", item);
|
||||
}
|
||||
|
||||
result.Receipt = receipt;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, $"[INVESTIGATE] Failed processing {itemAsString}.");
|
||||
if (item == null) return result;
|
||||
var newKey = item.DetermineAuditItemKey();
|
||||
if (!_inProgress.TryRemove(newKey, out item))
|
||||
{
|
||||
Log.Error("Couldn't remove item from concurrent dictionary. (Exception branch) {@item}", item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleItem(QueueItem item)
|
||||
{
|
||||
_inputQueue.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.API.Collections.RepoApi.Configuration;
|
||||
using LCU.API.Collections.RepoApi.DataAccess;
|
||||
using LCU.API.Collections.RepoApi.Services;
|
||||
using LCU.Shared.Collections.RepoApiLib.Hosted;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Producer;
|
||||
using LCU.Shared.Collections.RepoApiLib.Repositories;
|
||||
using LCU.Shared.Realtime.QueueLib.Models;
|
||||
using LCU.Shared.Realtime.QueueLib.Queue;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.HostedServices
|
||||
{
|
||||
public class RepoQueueProducerService : IRepoQueueProducerService
|
||||
{
|
||||
private readonly RepoApiConfiguration _configuration;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ICollectionsDao _collectionsDao;
|
||||
private readonly QueueItemProducer<ProcessingItem> _queueService;
|
||||
private readonly IQueueConsumerConfig _consumerConfig;
|
||||
private readonly IRepoAccountService _repoAccountService;
|
||||
private readonly IProcessingReceiptService _processingReceiptService;
|
||||
|
||||
public RepoQueueProducerService(IQueueConnectionConfig connectionConfig,
|
||||
IRepoAccountService repoAcountService,
|
||||
RepoApiConfiguration configuration,
|
||||
ISettingsService settingsService,
|
||||
ICollectionsDao collectionsDao,
|
||||
IProcessingReceiptService processingReceiptService)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_settingsService = settingsService;
|
||||
_collectionsDao = collectionsDao;
|
||||
_processingReceiptService = processingReceiptService;
|
||||
_consumerConfig = configuration.QueueConsumerConfigs["RepoAccountQueue"];
|
||||
_queueService = new QueueItemProducer<ProcessingItem>(connectionConfig);
|
||||
_repoAccountService = repoAcountService;
|
||||
}
|
||||
|
||||
public async Task ImportData(TaskSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var reprocessItems = await _processingReceiptService.GetRetryListAsync();
|
||||
var watchStatusItems = await RetrieveRepoAccountsOfInterest();
|
||||
var combinedList = CreateCombinedList(reprocessItems, watchStatusItems);
|
||||
|
||||
Log.Information($"Repo Producer Queueing {combinedList.Count} results.");
|
||||
if (combinedList.Any())
|
||||
{
|
||||
_queueService.EnqueueList(combinedList, _consumerConfig);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Error Deserializing. Repo Producer Failed to queue accounts.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var fullConfig = await _collectionsDao.RetrieveTemenosConfigurationAsync();
|
||||
var users = fullConfig.Users.Select(x => new ExternalUser()
|
||||
{
|
||||
Code = x.Code,
|
||||
ReferenceId = x.Id,
|
||||
Name = x.Description
|
||||
}).ToList();
|
||||
await _settingsService.SyncExternalUsersAsync(users);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Error Updating external user list from Temenos.");
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ProcessingItem> CreateCombinedList(IReadOnlyCollection<ProcessingReceipt> listPriority,
|
||||
IReadOnlyCollection<ProcessingItem> listSecondary)
|
||||
{
|
||||
var combinedList = new List<ProcessingItem>();
|
||||
|
||||
if (listPriority != null && listPriority.Any())
|
||||
{
|
||||
foreach (var processingItem in listPriority)
|
||||
{
|
||||
combinedList.Add(new ProcessingItem()
|
||||
{
|
||||
AccountNo = processingItem.AccountNo,
|
||||
AccountId = processingItem.AccountId,
|
||||
CaseId = processingItem.CaseId,
|
||||
QueuedBy = "RepoManager-Retry"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (listSecondary == null || !listSecondary.Any()) return combinedList;
|
||||
|
||||
foreach (var processingItem in listSecondary)
|
||||
{
|
||||
if (combinedList.Any(x => x.AccountNo == processingItem.AccountNo)) continue;
|
||||
|
||||
processingItem.QueuedBy = "RepoManager";
|
||||
combinedList.Add(processingItem);
|
||||
}
|
||||
|
||||
return combinedList;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ProcessingItem>> RetrieveRepoAccountsOfInterest()
|
||||
{
|
||||
var repoAccountNos = new List<ProcessingItem>();
|
||||
try
|
||||
{
|
||||
repoAccountNos.AddRange(await _repoAccountService.GetItemsForProcessing());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to Retrieve Repo Accounts For Polling in RepoQueueProducerService");
|
||||
}
|
||||
|
||||
return repoAccountNos;
|
||||
}
|
||||
|
||||
public void SendRequestToQueue(ProcessingItem queueRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
_queueService.Enqueue(queueRequest, _consumerConfig);
|
||||
Log.Debug($"Queued The Following Account {queueRequest.AccountNo}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to Add AccountNo To Queue {queueRequest.AccountNo}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>C:\dev\projects\LCU.API.Collections.RepoApi\LCU.API.Collections.RepoApi\LCU.API.Collections.RepoApi.xml</DocumentationFile>
|
||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>C:\dev\projects\LCU.API.Collections.RepoApi\LCU.API.Collections.RepoApi\LCU.API.Collections.RepoApi.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvTextFieldParser" Version="1.2.1" />
|
||||
<PackageReference Include="LCU.API.Collections.CollectionsApiLib" Version="1.11.0" />
|
||||
<PackageReference Include="LCU.Api.Internal.MaidBotLib" Version="1.0.0" />
|
||||
<PackageReference Include="LCU.Shared.Common.NotificationApiLib" Version="2.0.0" />
|
||||
<PackageReference Include="LCU.Shared.Realtime.QueueLib" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.FeatureManagement" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
<PackageReference Include="Serilog" Version="2.9.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
|
||||
<PackageReference Include="WinSCP" Version="5.15.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LCU.Shared.Collections.RepoApiLib\LCU.Shared.Collections.RepoApiLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Services\AutoIms\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="privatekey.ppk">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>LCU.API.Collections.RepoApi</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:LCU.API.Collections.RepoApi.AlertChecker.TimeInStatusChecker">
|
||||
This class can be used to create the alerts in the "Days + ' days in REHO. '" series. for both REHO and O4R
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Controllers.LoggingController.Post(LCU.Shared.Collections.RepoApiLib.Models.ClientError)">
|
||||
<summary>
|
||||
Use to send something to the logger from the client
|
||||
</summary>
|
||||
<remarks>
|
||||
Logs a message from a client.
|
||||
Cookies: None required or set.
|
||||
Security: Must have a valid JWT token retrieved from Azure AD, with ADMIN role.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="T:LCU.API.Collections.RepoApi.Domain.FloorPricing.Strategies.AutoImsSuggestedValue">
|
||||
This strategy uses the suggested price from the ImsAutoFileData entry for the auction.
|
||||
</member>
|
||||
<member name="T:LCU.API.Collections.RepoApi.Domain.FloorPricing.Strategies.NadaMmrConditionValue">
|
||||
This strategy uses the AutoIms MMR values, the values from Nada, and the condition from AutoIms along
|
||||
with a specific formula, to calculate a floor.
|
||||
</member>
|
||||
<member name="T:LCU.API.Collections.RepoApi.Exceptions.NotFoundException">
|
||||
If an entity cannot be found, that specific error can allow for a 404 or 400 with more detail to be
|
||||
returned. So specify that to the API layer.
|
||||
</member>
|
||||
<member name="T:LCU.API.Collections.RepoApi.Exceptions.ServerErrorException">
|
||||
Do not want to expose database and related exceptions to the controllers / web layer.
|
||||
The lower layers log what happened for developers, then return a general error; unless
|
||||
a more specific error can be returned in a way to allow the client to act (such as NotFound).
|
||||
</member>
|
||||
<member name="T:LCU.API.Collections.RepoApi.HostedServices.RepoQueueConsumerService">
|
||||
Special Notes.
|
||||
1. Direct injection of services cannot be done, the main API context is on it's own
|
||||
scope/thread. So instead, the scope factory is passed, so that this can create scopes.
|
||||
2. This is designed to use 5 threads, from within an IHostedService thread context, to
|
||||
process queues. It's designed to do this in 'batches' to simulate a batch job, which
|
||||
is where the creation of the RunInfo comes into scope.
|
||||
3. There is a main 'thread' here, which waits for a 'batch' of work to be complete.
|
||||
4. Under the main thread, there are 5 worker threads, communicating using two queues besides
|
||||
the main Rabbit queue. The queues are used to set something up for processing (_inputQueue)
|
||||
and to mark something as done processing (_outputQueue).
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.HostedServices.RepoQueueConsumerService.ExecuteAsync(System.Threading.CancellationToken)">
|
||||
This function is the main function run by the IHostedService interface, so
|
||||
should be considered the 'main' here.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.CreateAutoIMSAuction(LCU.Shared.Collections.RepoApiLib.Models.Collateral,LCU.Shared.Collections.RepoApiLib.WebServices.AutoIMS.VehicleLocation,LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
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.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.RemoveAuctionFromAutoIMS(LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
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.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.UpdateAuctionFloor(LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment,LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
This function will attempt to call AutoIMS with an auction floor, and will not update the
|
||||
floor related values unless it succeeds
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.UpdateTitleSentDate(LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment,LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
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
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.UpdateRedeemedDate(LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment,LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
This function will attempt to call AutoIMS and set the redeemed date, and will not update the
|
||||
redeemed date unless it succeeds
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AuctionAssignmentService.UpdateHoldData(LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment,LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignment)">
|
||||
This function will attempt to call AutoIMS to place a hold, and will not update the
|
||||
hold related values unless it succeeds
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AutoIms.AutoImsImporterService.ImportNotesToDatabase(System.Collections.Generic.IEnumerable{LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignmentNote})">
|
||||
A set of notes comes from AutoIMS and must be inserted (But not updated).
|
||||
Duplicates/Existing are checked for based on writer, time and text.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AutoIms.AutoImsImporterService.ImportDataFile(System.Collections.Generic.IEnumerable{LCU.Shared.Collections.RepoApiLib.Models.ImsAutoFileData})">
|
||||
Each row in the Data file is an ImsAutoAuctionData row used to track the status of
|
||||
a Repo at auction and assist in setting the floor.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.AutoIms.ImsAutoFileNoteConverter.CompressIdentifyingRows(System.Collections.Generic.IEnumerable{LCU.Shared.Collections.RepoApiLib.Models.AuctionAssignmentNote})">
|
||||
Take the rows with only account and discard them, using the account no to update the other rows.
|
||||
Each row in the CSV does not have CustomerAccountNo, only the first row of each set.
|
||||
Further, if a row has a customer account no, it has nothing else.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.IAccountAlertCheckService.AlertChecksPreStatusReject(LCU.Shared.Collections.RepoApiLib.Models.RepoAccount,LCU.Shared.Collections.RepoApiLib.Models.RepoAccount)">
|
||||
These alerts run before the account is checked to see if it was moved to an un-allowed status.
|
||||
What is this moves the status to an un-allowed status?
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.IAccountAlertCheckService.AlertChecksPrimary(LCU.Shared.Collections.RepoApiLib.Models.RepoAccount,LCU.Shared.Collections.RepoApiLib.Models.RepoAccount)">
|
||||
These alerts run after the account has been updated or created and is the primary set of alert checks.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.RepoAccountProcessorService.ConvertBorrowerType(System.String)">
|
||||
High Risk function because of string comparison and unknown mapping.
|
||||
The old system got the integer from the database.
|
||||
The new system gets a string from the web service.
|
||||
This maps the new string to the old integer, if borrower types start
|
||||
showing up wrong, look here...
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.RepoAccountProcessorService.UpdateFieldsOnStatusChange(LCU.Shared.Collections.RepoApiLib.Models.RepoAccount,LCU.Shared.Collections.RepoApiLib.Models.RepoAccount)">
|
||||
If the status changed to 04R from not-O4R.
|
||||
Is there a repo company assignment (even the default), if not assign one.
|
||||
If there if a repo company assignment, but the status has changed, forcibly re-assign default
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.RepoAccountProcessorService.SetNewO4RDataForAccount(LCU.Shared.Collections.RepoApiLib.Models.RepoAccount,LCU.Shared.Collections.RepoApiLib.Models.Collateral)">
|
||||
When an account changes to 04R, update all relevant fields/dates.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.Producer.RepoAccountProcessorService.UpdateCollateralStatus(LCU.Shared.Collections.RepoApiLib.Models.RepoAccount,LCU.Shared.Collections.RepoApiLib.Models.RepoAccount)">
|
||||
if the status was O4R and is now REPO, update date hooked (picked up).
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.RepoAccountService.SelectRepoAccount(System.Collections.Generic.IReadOnlyCollection{LCU.Shared.Collections.RepoApiLib.Models.RepoAccount})">
|
||||
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.
|
||||
</member>
|
||||
<member name="M:LCU.API.Collections.RepoApi.Services.StatusService.ConvertStatus(LCU.API.Collections.CollectionsApiLib.Models.RepoStatus)">
|
||||
TODO #7712
|
||||
Convert a Stage from a case to a RepoAccount status.
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Shared.Collections.RepoApiLib.WebServices.AutoIMS;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.MockServices
|
||||
{
|
||||
// Excluded from code coverage because this service is a mock.
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class MockAutoImsWebService : IAutoImsWebService
|
||||
{
|
||||
public async Task<WebServiceResponse> AddAuctionNote(string vin, string customerNumber, string note, string writer)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Auction note added"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public string GetCollateralImage(string conditionReporUrl)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> PlaceHold(string VIN, int holdCode, DateTime holdUntil)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Vehicle placed on hold"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> PostVehicleAsync<T>(T vehicle)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Vehicle posted to auction"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> Redeem(string VIN)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Vehicle set to redeemed"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> RemoveAuction(string VIN, string customerNumber)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Auction removed"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> RemoveFromAuction(string vin, string clientStockNumber)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Vehicle removed from auction"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> TitleSent(string VIN)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Floor price updated"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<WebServiceResponse> UpdateAuctionIdentifyingValuesAsync(string customerNum, string vin, string auctionAssignmentId)
|
||||
{
|
||||
return await Task.FromResult(new WebServiceResponse()
|
||||
{
|
||||
Status = new WebServiceResponseStatus()
|
||||
{
|
||||
Code = 1,
|
||||
Description = "Identifying values updated"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models
|
||||
{
|
||||
public class AppCorsConfig
|
||||
{
|
||||
public string[] CORS_AllowedOrigins { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace LCU.API.Collections.RepoApi.Models
|
||||
{
|
||||
public class AppUser
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string[] Roles { get; set; }
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Filters
|
||||
{
|
||||
public class AuctionAssignmentFilter : PageFilter
|
||||
{
|
||||
|
||||
[FromQuery(Name = "readyForFloorPrice")]
|
||||
public bool ReadyForFloorPrice { get; set; }
|
||||
|
||||
public AuctionAssignmentFilter() : base()
|
||||
{
|
||||
this.ReadyForFloorPrice = false;
|
||||
}
|
||||
|
||||
public override object Clone()
|
||||
{
|
||||
var jsonString = JsonConvert.SerializeObject(this);
|
||||
return JsonConvert.DeserializeObject(jsonString, this.GetType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Filters
|
||||
{
|
||||
public class PageFilter : ICloneable
|
||||
{
|
||||
[Range(1, 1000)]
|
||||
public int Page { get; set; }
|
||||
|
||||
[Range(1, 1000)]
|
||||
public int Limit { get; set; }
|
||||
|
||||
public PageFilter()
|
||||
{
|
||||
this.Page = 1;
|
||||
this.Limit = 100;
|
||||
}
|
||||
|
||||
public virtual object Clone()
|
||||
{
|
||||
var jsonString = JsonConvert.SerializeObject(this);
|
||||
return JsonConvert.DeserializeObject(jsonString, this.GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Reporting.Responses;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Reporting
|
||||
{
|
||||
public class ReportHeaderCellMap
|
||||
{
|
||||
public string Header { get; set; }
|
||||
public ReportCell Cell { get; set; }
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Requests
|
||||
{
|
||||
public class AccountQueueRequest : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
[StringLength(16, MinimumLength = 7,
|
||||
ErrorMessage = "The AccountNo field must have a length between 7 and 16 characters")]
|
||||
public string AccountNo { get; set; }
|
||||
|
||||
[StringLength(20, MinimumLength = 1)] public string CaseId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
var results = new List<ValidationResult>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(AccountNo))
|
||||
{
|
||||
results.Add(new ValidationResult(
|
||||
"AccountNo (AccountIdentifier) is not valid",
|
||||
new[] { nameof(AccountNo) }));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
using LCU.Shared.Collections.RepoApiLib.WebServices.AutoIMS;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Requests
|
||||
{
|
||||
public class AuctionAssignmentPostRequest : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
public Shared.Collections.RepoApiLib.Models.AuctionAssignment AuctionAssignment { get; set; }
|
||||
|
||||
[Required]
|
||||
public IReadOnlyCollection<Collateral> Collaterals { get; set; }
|
||||
|
||||
[Required]
|
||||
public VehicleLocation VehicleLocation { get; set; }
|
||||
|
||||
[Required]
|
||||
public int? PrimaryCollateralId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
var failedValidations = new List<ValidationResult>();
|
||||
|
||||
if (this.Collaterals.Count == 0)
|
||||
{
|
||||
failedValidations.Add(new ValidationResult("There must be at least one collateral when creating an auction assignment."));
|
||||
}
|
||||
|
||||
var primaryCollateral = Collaterals.Where(collateral => collateral.CollateralId == PrimaryCollateralId).FirstOrDefault();
|
||||
if (primaryCollateral == null)
|
||||
{
|
||||
failedValidations.Add(new ValidationResult("Collaterals must contain a piece of Collateral with a CollateralID that matches the PrimaryCollateralId."));
|
||||
}
|
||||
|
||||
return failedValidations;
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Requests
|
||||
{
|
||||
public class RepoAccountPostFilter
|
||||
{
|
||||
public List<int?> RepoIdList { get; set; }
|
||||
|
||||
public string Status { get; set; }
|
||||
|
||||
public string AuctionHouseId { get; set; }
|
||||
|
||||
public int? RepoCompanyId { get; set; }
|
||||
|
||||
public bool? OnlyOpenAccounts { get; set; }
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Requests
|
||||
{
|
||||
public class ReportingRequest
|
||||
{
|
||||
public DateTime LowDate { get; set; }
|
||||
public DateTime HighDate { get; set; }
|
||||
|
||||
public ReportingRequest()
|
||||
{
|
||||
HighDate = DateTime.Today;
|
||||
LowDate = HighDate.Subtract(TimeSpan.FromDays(365));
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Requests
|
||||
{
|
||||
public class VehicleFloorPricingParametersPutRequest
|
||||
{
|
||||
public VehicleFloorPricingParameters[] vehicleFloorPricingParameters;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Responses
|
||||
{
|
||||
public class FactoidResponse
|
||||
{
|
||||
public string CumulativeValue { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string FontAwesomeIcon { get; set; }
|
||||
public IReadOnlyCollection<RepoAccount> Records { get; set; }
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LCU.API.Collections.RepoApi.Models.Filters;
|
||||
using LCU.Shared.Collections.RepoApiLib.Models.Results;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Responses
|
||||
{
|
||||
public class PagedResponse<T> where T : class
|
||||
{
|
||||
public PagedResponse() { }
|
||||
|
||||
public PagedResponse(PagedResult<T> pagedResult, PageFilter filter, IUrlHelper url, HttpRequest request, string uriAction = "Get")
|
||||
{
|
||||
TotalItems = pagedResult.Total;
|
||||
Items = pagedResult.Items;
|
||||
CurrentPage = filter.Page;
|
||||
TotalPages = pagedResult.Total == 0 ? 1 : (int) Math.Ceiling((decimal) pagedResult.Total / filter.Limit);
|
||||
|
||||
PageFilter newFilter = filter.Clone() as PageFilter;
|
||||
if (pagedResult.Total > (filter.Page * filter.Limit))
|
||||
{
|
||||
newFilter.Page = filter.Page + 1;
|
||||
NextPage = new Uri(url.Action(uriAction, null, newFilter, request.Scheme));
|
||||
}
|
||||
|
||||
if (filter.Page > 1)
|
||||
{
|
||||
newFilter.Page = filter.Page - 1;
|
||||
PreviousPage = new Uri(url.Action(uriAction, null, newFilter, request.Scheme));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<T> Items { get; set; }
|
||||
public Uri NextPage { get; set; }
|
||||
public Uri PreviousPage { get; set; }
|
||||
public int TotalItems { get; set; }
|
||||
public int CurrentPage { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Responses
|
||||
{
|
||||
public class RepoCompanySelectionPostResponse
|
||||
{
|
||||
public RepoCompanySelection PreviousRepoCompanySelection {get; set;}
|
||||
public RepoCompanySelection NewRepoCompanySelection { get; set; }
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using LCU.Shared.Collections.RepoApiLib.Models;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Responses
|
||||
{
|
||||
public class VehicleFloorPricingParametersPutResponse
|
||||
{
|
||||
public VehicleFloorPricingParameters[] vehicleFloorPricingParameters;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Results
|
||||
{
|
||||
public class DateTimeResult : IResult<DateTime?>
|
||||
{
|
||||
public DateTime? Value { get; set; } = null;
|
||||
public string ErrorMessage { get; set; } = null;
|
||||
public bool IsSuccess { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models.Results
|
||||
{
|
||||
public interface IResult<T>
|
||||
{
|
||||
T Value { get; set; }
|
||||
bool IsSuccess { get; set; }
|
||||
String ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi.Models
|
||||
{
|
||||
public class SqlOptionsConfig
|
||||
{
|
||||
public int MaxRetryCount { get; set; }
|
||||
public TimeSpan MaxRetryDelay { get; set; }
|
||||
public List<int> ErrorNumbersToAdd { get; set; }
|
||||
public bool EnableDetailedErrors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace LCU.API.Collections.RepoApi
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>().UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
|
||||
.ReadFrom.Configuration(hostingContext.Configuration));
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:22532",
|
||||
"sslPort": 44380
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"LCU.API.Collections.RepoApi": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
}
|
||||
}
|
||||
}
|
||||
+461
@@ -0,0 +1,461 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user