diff --git a/LCU.API.Internal.RepoApi/.gitignore b/LCU.API.Internal.RepoApi/.gitignore new file mode 100644 index 0000000..41ee9e4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/.gitignore @@ -0,0 +1,363 @@ +## 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 + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# 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/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# 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 + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# 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/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +.idea/** diff --git a/LCU.API.Internal.RepoApi/Dockerfile b/LCU.API.Internal.RepoApi/Dockerfile new file mode 100644 index 0000000..0198193 --- /dev/null +++ b/LCU.API.Internal.RepoApi/Dockerfile @@ -0,0 +1,41 @@ +# .NET Core SDK +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS aspnetcore-build + +# Copy Projects +COPY LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj ./source/LCU.API.Internal.RepoApi/ +COPY LCU.API.Internal.RepoApi.Repos/LCU.API.Internal.RepoApi.Repos.csproj ./source/LCU.API.Internal.RepoApi.Repos/ +COPY LCU.Library.Internal.RepoApiModels/LCU.Library.Internal.RepoApiModels.csproj ./source/LCU.Library.Internal.RepoApiModels/ + +# Install Credential Provider and set env variables to enable Nuget restore with auth +ARG PAT=bh2lq3jibktw67b2wdbqyfln7cd3je6lk52kijntpj43v6kimpgq +RUN curl -L https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | sh +#RUN wget -qO- https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | bash +ENV NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED true +ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS "{\"endpointCredentials\": [{\"endpoint\":\"https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json\", \"password\":\"${PAT}\"}]}" + +# .NET Core Restore, use auth'd feed +RUN dotnet restore -s "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json" -s "https://api.nuget.org/v3/index.json" ./source/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj + +# Copy All Files +COPY . ./source/ + +FROM aspnetcore-build AS test +LABEL test=true +COPY LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj ./source/LCU.API.Internal.RepoApiTests/ +RUN dotnet restore ./source/LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj +RUN dotnet test -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.trx" ./source/LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj + +# .NET Core Publish +FROM aspnetcore-build as publish +RUN dotnet publish ./source/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj -c Release -o /dist + +# ASP.NET Core +FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.20 +RUN apk add --no-cache icu-libs && apk add --no-cache tzdata; +ENV TZ America/Chicago +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false +ENV ASPNETCORE_ENVIRONMENT=Development + +WORKDIR /app +COPY --from=publish /dist . +ENTRYPOINT ["dotnet", "LCU.API.Internal.RepoApi.dll"] diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/ConnectedService.json b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/ConnectedService.json new file mode 100644 index 0000000..6e94d95 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/ConnectedService.json @@ -0,0 +1,16 @@ +{ + "ExtendedData": { + "inputs": [ + "https://webservice-stage.jdpowervalues.com/vehicles/secure/securelogin.asmx?wsdl" + ], + "collectionTypes": [ + "System.Array", + "System.Collections.Generic.Dictionary`2" + ], + "namespaceMappings": [ + "*, JdpLogin" + ], + "targetFramework": "netcoreapp2.2", + "typeReuseMode": "All" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/Reference.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/Reference.cs new file mode 100644 index 0000000..ee91900 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/Reference.cs @@ -0,0 +1,258 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JdpLogin +{ + using System.Runtime.Serialization; + + + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Runtime.Serialization.DataContractAttribute(Name="GetTokenRequest", Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetTokenRequest : object + { + + private string UsernameField; + + private string PasswordField; + + [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)] + public string Username + { + get + { + return this.UsernameField; + } + set + { + this.UsernameField = value; + } + } + + [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=1)] + public string Password + { + get + { + return this.PasswordField; + } + set + { + this.PasswordField = value; + } + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ServiceModel.ServiceContractAttribute(Namespace="http://webservice.jdpowervalues.com/", ConfigurationName="JdpLogin.SecureLoginSoap")] + public interface SecureLoginSoap + { + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getToken", ReplyAction="*")] + System.Threading.Tasks.Task getTokenAsync(JdpLogin.getTokenRequest1 request); + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + [System.ServiceModel.MessageContractAttribute(IsWrapped=false)] + public partial class getTokenRequest1 + { + + [System.ServiceModel.MessageBodyMemberAttribute(Name="getToken", Namespace="http://webservice.jdpowervalues.com/", Order=0)] + public JdpLogin.getTokenRequest1Body Body; + + public getTokenRequest1() + { + } + + public getTokenRequest1(JdpLogin.getTokenRequest1Body Body) + { + this.Body = Body; + } + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + [System.Runtime.Serialization.DataContractAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class getTokenRequest1Body + { + + [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=0)] + public JdpLogin.GetTokenRequest tokenRequest; + + public getTokenRequest1Body() + { + } + + public getTokenRequest1Body(JdpLogin.GetTokenRequest tokenRequest) + { + this.tokenRequest = tokenRequest; + } + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + [System.ServiceModel.MessageContractAttribute(IsWrapped=false)] + public partial class getTokenResponse + { + + [System.ServiceModel.MessageBodyMemberAttribute(Name="getTokenResponse", Namespace="http://webservice.jdpowervalues.com/", Order=0)] + public JdpLogin.getTokenResponseBody Body; + + public getTokenResponse() + { + } + + public getTokenResponse(JdpLogin.getTokenResponseBody Body) + { + this.Body = Body; + } + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + [System.Runtime.Serialization.DataContractAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class getTokenResponseBody + { + + [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=0)] + public string getTokenResult; + + public getTokenResponseBody() + { + } + + public getTokenResponseBody(string getTokenResult) + { + this.getTokenResult = getTokenResult; + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + public interface SecureLoginSoapChannel : JdpLogin.SecureLoginSoap, System.ServiceModel.IClientChannel + { + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + public partial class SecureLoginSoapClient : System.ServiceModel.ClientBase, JdpLogin.SecureLoginSoap + { + + /// + /// Implement this partial method to configure the service endpoint. + /// + /// The endpoint to configure + /// The client credentials + static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials); + + public SecureLoginSoapClient(EndpointConfiguration endpointConfiguration) : + base(SecureLoginSoapClient.GetBindingForEndpoint(endpointConfiguration), SecureLoginSoapClient.GetEndpointAddress(endpointConfiguration)) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public SecureLoginSoapClient(EndpointConfiguration endpointConfiguration, string remoteAddress) : + base(SecureLoginSoapClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress)) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public SecureLoginSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : + base(SecureLoginSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public SecureLoginSoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : + base(binding, remoteAddress) + { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + System.Threading.Tasks.Task JdpLogin.SecureLoginSoap.getTokenAsync(JdpLogin.getTokenRequest1 request) + { + return base.Channel.getTokenAsync(request); + } + + public System.Threading.Tasks.Task getTokenAsync(JdpLogin.GetTokenRequest tokenRequest) + { + JdpLogin.getTokenRequest1 inValue = new JdpLogin.getTokenRequest1(); + inValue.Body = new JdpLogin.getTokenRequest1Body(); + inValue.Body.tokenRequest = tokenRequest; + return ((JdpLogin.SecureLoginSoap)(this)).getTokenAsync(inValue); + } + + public virtual System.Threading.Tasks.Task OpenAsync() + { + return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndOpen)); + } + + public virtual System.Threading.Tasks.Task CloseAsync() + { + return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginClose(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndClose)); + } + + private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration) + { + if ((endpointConfiguration == EndpointConfiguration.SecureLoginSoap)) + { + System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding(); + result.MaxBufferSize = int.MaxValue; + result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max; + result.MaxReceivedMessageSize = int.MaxValue; + result.AllowCookies = true; + result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport; + return result; + } + if ((endpointConfiguration == EndpointConfiguration.SecureLoginSoap12)) + { + System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding(); + System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement(); + textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None); + result.Elements.Add(textBindingElement); + System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement(); + httpsBindingElement.AllowCookies = true; + httpsBindingElement.MaxBufferSize = int.MaxValue; + httpsBindingElement.MaxReceivedMessageSize = int.MaxValue; + result.Elements.Add(httpsBindingElement); + return result; + } + throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration)); + } + + private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration) + { + if ((endpointConfiguration == EndpointConfiguration.SecureLoginSoap)) + { + return new System.ServiceModel.EndpointAddress("https://webservice-stage.jdpowervalues.com/vehicles/secure/securelogin.asmx"); + } + if ((endpointConfiguration == EndpointConfiguration.SecureLoginSoap12)) + { + return new System.ServiceModel.EndpointAddress("https://webservice-stage.jdpowervalues.com/vehicles/secure/securelogin.asmx"); + } + throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration)); + } + + public enum EndpointConfiguration + { + + SecureLoginSoap, + + SecureLoginSoap12, + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/securelogin.wsdl b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/securelogin.wsdl new file mode 100644 index 0000000..a4bfdb5 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpLogin/securelogin.wsdl @@ -0,0 +1,74 @@ + + + This is JDPA Web Service into its Login methods. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Logs the user in and returns a valid token. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is JDPA Web Service into its Login methods. + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/ConnectedService.json b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/ConnectedService.json new file mode 100644 index 0000000..74d9b37 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/ConnectedService.json @@ -0,0 +1,16 @@ +{ + "ExtendedData": { + "inputs": [ + "http://webservice-stage.jdpowervalues.com/vehicles/vehicle.asmx?wsdl" + ], + "collectionTypes": [ + "System.Array", + "System.Collections.Generic.Dictionary`2" + ], + "namespaceMappings": [ + "*, JdpVehicle" + ], + "targetFramework": "netcoreapp2.2", + "typeReuseMode": "All" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/Reference.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/Reference.cs new file mode 100644 index 0000000..9ca6bee --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/Reference.cs @@ -0,0 +1,3503 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JdpVehicle +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.ServiceModel.ServiceContractAttribute(Namespace="http://webservice.jdpowervalues.com/", ConfigurationName="JdpVehicle.VehicleSoap")] + public interface VehicleSoap + { + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/ping", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + System.Threading.Tasks.Task pingAsync(string token); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getYears", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getYearsAsync(JdpVehicle.GetYearsRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getMakes", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getMakesAsync(JdpVehicle.GetMakesRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getSeries", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getSeriesAsync(JdpVehicle.GetSeriesRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getBodyUids", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getBodyUidsAsync(JdpVehicle.GetBodyUidsRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getVehicles", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getVehiclesAsync(JdpVehicle.GetVehiclesByVinRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getVehicle", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getVehicleAsync(JdpVehicle.GetVehicleByUidRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getVehicleByVic", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getVehicleByVicAsync(JdpVehicle.GetVehicleByVicRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getAccessories", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getAccessoriesAsync(JdpVehicle.GetAccessoriesRequest accessoriesRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getInclusiveAccessories", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getInclusiveAccessoriesAsync(JdpVehicle.GetMutualAccessoriesRequest mutualAccessoriesRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getExclusiveAccessories", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getExclusiveAccessoriesAsync(JdpVehicle.GetMutualAccessoriesRequest mutualAccessoriesRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/validateVin", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task validateVinAsync(JdpVehicle.ValidateVinRequest l_request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getMileageAdj", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getMileageAdjAsync(JdpVehicle.GetMileageAdjByUidRequest mileageAdjRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getRegions", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getRegionsAsync(JdpVehicle.GetRegionsRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getRegionByStateCode", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getRegionByStateCodeAsync(JdpVehicle.GetRegionByStateCodeRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getTotalAdjFloorValues", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getTotalAdjFloorValuesAsync(JdpVehicle.GetVehicleValuesByUidRequest l_Request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getVehicleValueByUid", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getVehicleValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getBaseVehicleValueByUid", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getBaseVehicleValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getVehicleAndValueByUid", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getVehicleAndValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getDefaultVehicleAndValueByVin", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getDefaultVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getMsrpVehicleAndValueByVin", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getMsrpVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByMsrpRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getHighVehicleAndValueByVin", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getHighVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getLowVehicleAndValueByVin", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getLowVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getAdjustedValuesByUid", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getAdjustedValuesByUidAsync(JdpVehicle.GetAdjustedValuesByUidRequest request); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getAuctionValues", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getAuctionValuesAsync(JdpVehicle.GetAuctionValuesRequest auctionValuesRequest); + + [System.ServiceModel.OperationContractAttribute(Action="http://webservice.jdpowervalues.com/getExperianAutoCheckReport", ReplyAction="*")] + [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] + [System.ServiceModel.ServiceKnownTypeAttribute(typeof(NADAValueRequest))] + System.Threading.Tasks.Task getExperianAutoCheckReportAsync(JdpVehicle.GetExperianAutoCheckRequest experianAutocheckRequest); + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetYearsRequest : NADAValueRequest + { + } + + /// + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetAuctionValuesRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehicleValuesByMsrpRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehicleValuesByVinRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehicleValuesByUidRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetAdjustedValuesByUidRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetRegionByStateCodeRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetRegionsRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetMileageAdjByUidRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(ValidateVinRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetMutualAccessoriesRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetAccessoriesRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehicleByVicRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehicleByUidRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetVehiclesByVinRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetBodyUidsRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetSeriesRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetMakesRequest))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetYearsRequest))] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public abstract partial class NADAValueRequest + { + + private string tokenField; + + private int periodField; + + private VehicleTypes vehicleTypeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Token + { + get + { + return this.tokenField; + } + set + { + this.tokenField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Period + { + get + { + return this.periodField; + } + set + { + this.periodField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public VehicleTypes VehicleType + { + get + { + return this.vehicleTypeField; + } + set + { + this.vehicleTypeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public enum VehicleTypes + { + + /// + UsedCar, + + /// + CommercialTruck, + + /// + Motorcycle, + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetExperianAutoCheckRequest + { + + private string tokenField; + + private string experianAccountNumberField; + + private string vinField; + + private ExperianAutoCheckReportTypes reportTypeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Token + { + get + { + return this.tokenField; + } + set + { + this.tokenField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string ExperianAccountNumber + { + get + { + return this.experianAccountNumberField; + } + set + { + this.experianAccountNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public ExperianAutoCheckReportTypes ReportType + { + get + { + return this.reportTypeField; + } + set + { + this.reportTypeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public enum ExperianAutoCheckReportTypes + { + + /// + VehicleHistory, + + /// + Summary, + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AdjustedAccessories + { + + private string accCodeField; + + private string accDescField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string AccCode + { + get + { + return this.accCodeField; + } + set + { + this.accCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string AccDesc + { + get + { + return this.accDescField; + } + set + { + this.accDescField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AuctionValue_Struc + { + + private int lowAdjustedAuctionValueField; + + private int averageAdjustedAuctionValueField; + + private int highAdjustedAuctionValueField; + + private string dateRangeField; + + private AdjustedAccessories[] accessoriesAdjustedListField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int LowAdjustedAuctionValue + { + get + { + return this.lowAdjustedAuctionValueField; + } + set + { + this.lowAdjustedAuctionValueField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int AverageAdjustedAuctionValue + { + get + { + return this.averageAdjustedAuctionValueField; + } + set + { + this.averageAdjustedAuctionValueField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int HighAdjustedAuctionValue + { + get + { + return this.highAdjustedAuctionValueField; + } + set + { + this.highAdjustedAuctionValueField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public string DateRange + { + get + { + return this.dateRangeField; + } + set + { + this.dateRangeField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayAttribute(Order=4)] + public AdjustedAccessories[] AccessoriesAdjustedList + { + get + { + return this.accessoriesAdjustedListField; + } + set + { + this.accessoriesAdjustedListField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AdjustedAccessory_Struc + { + + private string vACField; + + private string descriptionField; + + private decimal tradeInField; + + private decimal loanField; + + private decimal retailField; + + private bool isWithBodyField; + + private bool isWithPackageField; + + private bool isIncludedField; + + private bool isEnabledField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string VAC + { + get + { + return this.vACField; + } + set + { + this.vACField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string Description + { + get + { + return this.descriptionField; + } + set + { + this.descriptionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public decimal TradeIn + { + get + { + return this.tradeInField; + } + set + { + this.tradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal Loan + { + get + { + return this.loanField; + } + set + { + this.loanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public decimal Retail + { + get + { + return this.retailField; + } + set + { + this.retailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public bool IsWithBody + { + get + { + return this.isWithBodyField; + } + set + { + this.isWithBodyField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public bool IsWithPackage + { + get + { + return this.isWithPackageField; + } + set + { + this.isWithPackageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public bool IsIncluded + { + get + { + return this.isIncludedField; + } + set + { + this.isIncludedField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=8)] + public bool IsEnabled + { + get + { + return this.isEnabledField; + } + set + { + this.isEnabledField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AdjustedValues_Struc + { + + private int periodField; + + private int uIDField; + + private string vinField; + + private int regionField; + + private int mileageField; + + private decimal msrpField; + + private int avgMileageField; + + private decimal mileageAdjustmentField; + + private decimal accessoryAdjustmentTradeInField; + + private decimal accessoryAdjustmentLoanField; + + private decimal accessoryAdjustmentRetailField; + + private decimal baseRoughTradeInField; + + private decimal baseAvgTradeInField; + + private decimal baseCleanTradeInField; + + private decimal baseLoanField; + + private decimal baseRetailField; + + private decimal adjustedRoughTradeInField; + + private decimal adjustedAvgTradeInField; + + private decimal adjustedCleanTradeInField; + + private decimal adjustedLoanField; + + private decimal adjustedRetailField; + + private AdjustedAccessory_Struc[] accessoryListField; + + private BusinessRuleViolationErrorCodes businessRuleViolationErrorCodeField; + + private string businessRuleViolationErrorMessageField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Period + { + get + { + return this.periodField; + } + set + { + this.periodField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int UID + { + get + { + return this.uIDField; + } + set + { + this.uIDField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public int Mileage + { + get + { + return this.mileageField; + } + set + { + this.mileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public decimal Msrp + { + get + { + return this.msrpField; + } + set + { + this.msrpField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public int AvgMileage + { + get + { + return this.avgMileageField; + } + set + { + this.avgMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public decimal MileageAdjustment + { + get + { + return this.mileageAdjustmentField; + } + set + { + this.mileageAdjustmentField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=8)] + public decimal AccessoryAdjustmentTradeIn + { + get + { + return this.accessoryAdjustmentTradeInField; + } + set + { + this.accessoryAdjustmentTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=9)] + public decimal AccessoryAdjustmentLoan + { + get + { + return this.accessoryAdjustmentLoanField; + } + set + { + this.accessoryAdjustmentLoanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=10)] + public decimal AccessoryAdjustmentRetail + { + get + { + return this.accessoryAdjustmentRetailField; + } + set + { + this.accessoryAdjustmentRetailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=11)] + public decimal BaseRoughTradeIn + { + get + { + return this.baseRoughTradeInField; + } + set + { + this.baseRoughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=12)] + public decimal BaseAvgTradeIn + { + get + { + return this.baseAvgTradeInField; + } + set + { + this.baseAvgTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=13)] + public decimal BaseCleanTradeIn + { + get + { + return this.baseCleanTradeInField; + } + set + { + this.baseCleanTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=14)] + public decimal BaseLoan + { + get + { + return this.baseLoanField; + } + set + { + this.baseLoanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=15)] + public decimal BaseRetail + { + get + { + return this.baseRetailField; + } + set + { + this.baseRetailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=16)] + public decimal AdjustedRoughTradeIn + { + get + { + return this.adjustedRoughTradeInField; + } + set + { + this.adjustedRoughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=17)] + public decimal AdjustedAvgTradeIn + { + get + { + return this.adjustedAvgTradeInField; + } + set + { + this.adjustedAvgTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=18)] + public decimal AdjustedCleanTradeIn + { + get + { + return this.adjustedCleanTradeInField; + } + set + { + this.adjustedCleanTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=19)] + public decimal AdjustedLoan + { + get + { + return this.adjustedLoanField; + } + set + { + this.adjustedLoanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=20)] + public decimal AdjustedRetail + { + get + { + return this.adjustedRetailField; + } + set + { + this.adjustedRetailField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayAttribute(Order=21)] + public AdjustedAccessory_Struc[] AccessoryList + { + get + { + return this.accessoryListField; + } + set + { + this.accessoryListField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=22)] + public BusinessRuleViolationErrorCodes BusinessRuleViolationErrorCode + { + get + { + return this.businessRuleViolationErrorCodeField; + } + set + { + this.businessRuleViolationErrorCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=23)] + public string BusinessRuleViolationErrorMessage + { + get + { + return this.businessRuleViolationErrorMessageField; + } + set + { + this.businessRuleViolationErrorMessageField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public enum BusinessRuleViolationErrorCodes + { + + /// + NoError, + + /// + MutuallyExclusive, + + /// + UidAndVinMismatch, + + /// + VacCodeNotFound, + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class VehicleValue_Struc + { + + private int uidField; + + private int vehicleYearField; + + private int makeCodeField; + + private string makeDescrField; + + private int seriesCodeField; + + private string seriesDescrField; + + private int bodyCodeField; + + private string bodyDescrField; + + private int weightField; + + private decimal msrpField; + + private int aveMileageField; + + private decimal mileageAdjField; + + private decimal tradeInField; + + private decimal tradeInPlusVinAccField; + + private decimal tradeInPlusVinAccMileageField; + + private decimal loanField; + + private decimal loanPlusVinAccField; + + private decimal loanPlusVinAccMileageField; + + private decimal retailField; + + private decimal retailPlusVinAccField; + + private decimal retailPlusVinAccMileageField; + + private decimal avgTradeInField; + + private decimal avgTradeInPlusVinAccField; + + private decimal avgTradeInPlusVinAccMileageField; + + private decimal roughTradeInField; + + private decimal roughTradeInPlusVinAccField; + + private decimal roughTradeInPlusVinAccMileageField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int VehicleYear + { + get + { + return this.vehicleYearField; + } + set + { + this.vehicleYearField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int MakeCode + { + get + { + return this.makeCodeField; + } + set + { + this.makeCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public string MakeDescr + { + get + { + return this.makeDescrField; + } + set + { + this.makeDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public int SeriesCode + { + get + { + return this.seriesCodeField; + } + set + { + this.seriesCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public string SeriesDescr + { + get + { + return this.seriesDescrField; + } + set + { + this.seriesDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public int BodyCode + { + get + { + return this.bodyCodeField; + } + set + { + this.bodyCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public string BodyDescr + { + get + { + return this.bodyDescrField; + } + set + { + this.bodyDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=8)] + public int Weight + { + get + { + return this.weightField; + } + set + { + this.weightField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=9)] + public decimal Msrp + { + get + { + return this.msrpField; + } + set + { + this.msrpField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=10)] + public int AveMileage + { + get + { + return this.aveMileageField; + } + set + { + this.aveMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=11)] + public decimal MileageAdj + { + get + { + return this.mileageAdjField; + } + set + { + this.mileageAdjField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=12)] + public decimal TradeIn + { + get + { + return this.tradeInField; + } + set + { + this.tradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=13)] + public decimal TradeInPlusVinAcc + { + get + { + return this.tradeInPlusVinAccField; + } + set + { + this.tradeInPlusVinAccField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=14)] + public decimal TradeInPlusVinAccMileage + { + get + { + return this.tradeInPlusVinAccMileageField; + } + set + { + this.tradeInPlusVinAccMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=15)] + public decimal Loan + { + get + { + return this.loanField; + } + set + { + this.loanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=16)] + public decimal LoanPlusVinAcc + { + get + { + return this.loanPlusVinAccField; + } + set + { + this.loanPlusVinAccField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=17)] + public decimal LoanPlusVinAccMileage + { + get + { + return this.loanPlusVinAccMileageField; + } + set + { + this.loanPlusVinAccMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=18)] + public decimal Retail + { + get + { + return this.retailField; + } + set + { + this.retailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=19)] + public decimal RetailPlusVinAcc + { + get + { + return this.retailPlusVinAccField; + } + set + { + this.retailPlusVinAccField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=20)] + public decimal RetailPlusVinAccMileage + { + get + { + return this.retailPlusVinAccMileageField; + } + set + { + this.retailPlusVinAccMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=21)] + public decimal AvgTradeIn + { + get + { + return this.avgTradeInField; + } + set + { + this.avgTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=22)] + public decimal AvgTradeInPlusVinAcc + { + get + { + return this.avgTradeInPlusVinAccField; + } + set + { + this.avgTradeInPlusVinAccField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=23)] + public decimal AvgTradeInPlusVinAccMileage + { + get + { + return this.avgTradeInPlusVinAccMileageField; + } + set + { + this.avgTradeInPlusVinAccMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=24)] + public decimal RoughTradeIn + { + get + { + return this.roughTradeInField; + } + set + { + this.roughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=25)] + public decimal RoughTradeInPlusVinAcc + { + get + { + return this.roughTradeInPlusVinAccField; + } + set + { + this.roughTradeInPlusVinAccField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=26)] + public decimal RoughTradeInPlusVinAccMileage + { + get + { + return this.roughTradeInPlusVinAccMileageField; + } + set + { + this.roughTradeInPlusVinAccMileageField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class StandardValue_Struc + { + + private int uidField; + + private decimal retailField; + + private decimal tradeInField; + + private decimal loanField; + + private decimal roughTradeInField; + + private decimal avgTradeInField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public decimal Retail + { + get + { + return this.retailField; + } + set + { + this.retailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public decimal TradeIn + { + get + { + return this.tradeInField; + } + set + { + this.tradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal Loan + { + get + { + return this.loanField; + } + set + { + this.loanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public decimal RoughTradeIn + { + get + { + return this.roughTradeInField; + } + set + { + this.roughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public decimal AvgTradeIn + { + get + { + return this.avgTradeInField; + } + set + { + this.avgTradeInField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class Value_Struc + { + + private int uidField; + + private decimal msrpField; + + private int aveMileageField; + + private decimal mileageAdjField; + + private decimal retailField; + + private decimal tradeInField; + + private decimal loanField; + + private decimal mileAdjRetailField; + + private decimal mileAdjTradeInField; + + private decimal mileAdjLoanField; + + private decimal avgTradeInField; + + private decimal roughTradeInField; + + private decimal mileAdjAvgTradeInField; + + private decimal mileAdjRoughTradeInField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public decimal Msrp + { + get + { + return this.msrpField; + } + set + { + this.msrpField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int AveMileage + { + get + { + return this.aveMileageField; + } + set + { + this.aveMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal MileageAdj + { + get + { + return this.mileageAdjField; + } + set + { + this.mileageAdjField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public decimal Retail + { + get + { + return this.retailField; + } + set + { + this.retailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public decimal TradeIn + { + get + { + return this.tradeInField; + } + set + { + this.tradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public decimal Loan + { + get + { + return this.loanField; + } + set + { + this.loanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public decimal MileAdjRetail + { + get + { + return this.mileAdjRetailField; + } + set + { + this.mileAdjRetailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=8)] + public decimal MileAdjTradeIn + { + get + { + return this.mileAdjTradeInField; + } + set + { + this.mileAdjTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=9)] + public decimal MileAdjLoan + { + get + { + return this.mileAdjLoanField; + } + set + { + this.mileAdjLoanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=10)] + public decimal AvgTradeIn + { + get + { + return this.avgTradeInField; + } + set + { + this.avgTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=11)] + public decimal RoughTradeIn + { + get + { + return this.roughTradeInField; + } + set + { + this.roughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=12)] + public decimal MileAdjAvgTradeIn + { + get + { + return this.mileAdjAvgTradeInField; + } + set + { + this.mileAdjAvgTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=13)] + public decimal MileAdjRoughTradeIn + { + get + { + return this.mileAdjRoughTradeInField; + } + set + { + this.mileAdjRoughTradeInField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class FloorValue_Struc + { + + private int uidField; + + private decimal adjustedRetailField; + + private decimal adjustedTradeInField; + + private decimal adjustedLoanField; + + private decimal adjustedRoughTradeInField; + + private decimal adjustedAvgTradeInField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public decimal AdjustedRetail + { + get + { + return this.adjustedRetailField; + } + set + { + this.adjustedRetailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public decimal AdjustedTradeIn + { + get + { + return this.adjustedTradeInField; + } + set + { + this.adjustedTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal AdjustedLoan + { + get + { + return this.adjustedLoanField; + } + set + { + this.adjustedLoanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public decimal AdjustedRoughTradeIn + { + get + { + return this.adjustedRoughTradeInField; + } + set + { + this.adjustedRoughTradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public decimal AdjustedAvgTradeIn + { + get + { + return this.adjustedAvgTradeInField; + } + set + { + this.adjustedAvgTradeInField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class MileageTable_Struc + { + + private int lowLevelField; + + private int highLevelField; + + private decimal adjAmountField; + + private decimal ratePerMileField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int LowLevel + { + get + { + return this.lowLevelField; + } + set + { + this.lowLevelField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int HighLevel + { + get + { + return this.highLevelField; + } + set + { + this.highLevelField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public decimal AdjAmount + { + get + { + return this.adjAmountField; + } + set + { + this.adjAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal RatePerMile + { + get + { + return this.ratePerMileField; + } + set + { + this.ratePerMileField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class Mileage_Struc + { + + private decimal maxAmountField; + + private decimal minAmountField; + + private int averageMileageField; + + private int countField; + + private MileageTable_Struc[] mileageTableField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public decimal MaxAmount + { + get + { + return this.maxAmountField; + } + set + { + this.maxAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public decimal MinAmount + { + get + { + return this.minAmountField; + } + set + { + this.minAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int AverageMileage + { + get + { + return this.averageMileageField; + } + set + { + this.averageMileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public int Count + { + get + { + return this.countField; + } + set + { + this.countField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayAttribute(Order=4)] + public MileageTable_Struc[] MileageTable + { + get + { + return this.mileageTableField; + } + set + { + this.mileageTableField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AccessoryMultExcl_Struc + { + + private string accCodeField; + + private string accMultExclCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string AccCode + { + get + { + return this.accCodeField; + } + set + { + this.accCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string AccMultExclCode + { + get + { + return this.accMultExclCodeField; + } + set + { + this.accMultExclCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class AccessoryPakgIncl_Struc + { + + private string accCodeField; + + private string accPakgInclCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string AccCode + { + get + { + return this.accCodeField; + } + set + { + this.accCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string AccPakgInclCode + { + get + { + return this.accPakgInclCodeField; + } + set + { + this.accPakgInclCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class Accessory_Struc + { + + private int uidField; + + private string accDescField; + + private string accCodeField; + + private decimal tradeInField; + + private decimal retailField; + + private decimal loanField; + + private bool isIncludedField; + + private bool isAddedField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string AccDesc + { + get + { + return this.accDescField; + } + set + { + this.accDescField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public string AccCode + { + get + { + return this.accCodeField; + } + set + { + this.accCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public decimal TradeIn + { + get + { + return this.tradeInField; + } + set + { + this.tradeInField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public decimal Retail + { + get + { + return this.retailField; + } + set + { + this.retailField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public decimal Loan + { + get + { + return this.loanField; + } + set + { + this.loanField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public bool IsIncluded + { + get + { + return this.isIncludedField; + } + set + { + this.isIncludedField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public bool IsAdded + { + get + { + return this.isAddedField; + } + set + { + this.isAddedField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class Vehicle_Struc + { + + private int uidField; + + private int yearField; + + private int makeCodeField; + + private string makeDescrField; + + private int seriesCodeField; + + private string seriesDescrField; + + private int bodyCodeField; + + private string bodyDescrField; + + private int weightField; + + private string mileageClassField; + + private int gvwField; + + private int gcwField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Year + { + get + { + return this.yearField; + } + set + { + this.yearField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int MakeCode + { + get + { + return this.makeCodeField; + } + set + { + this.makeCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public string MakeDescr + { + get + { + return this.makeDescrField; + } + set + { + this.makeDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public int SeriesCode + { + get + { + return this.seriesCodeField; + } + set + { + this.seriesCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public string SeriesDescr + { + get + { + return this.seriesDescrField; + } + set + { + this.seriesDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public int BodyCode + { + get + { + return this.bodyCodeField; + } + set + { + this.bodyCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=7)] + public string BodyDescr + { + get + { + return this.bodyDescrField; + } + set + { + this.bodyDescrField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=8)] + public int Weight + { + get + { + return this.weightField; + } + set + { + this.weightField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=9)] + public string MileageClass + { + get + { + return this.mileageClassField; + } + set + { + this.mileageClassField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=10)] + public int Gvw + { + get + { + return this.gvwField; + } + set + { + this.gvwField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=11)] + public int Gcw + { + get + { + return this.gcwField; + } + set + { + this.gcwField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class Lookup_Struc + { + + private int codeField; + + private string descrField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Code + { + get + { + return this.codeField; + } + set + { + this.codeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string Descr + { + get + { + return this.descrField; + } + set + { + this.descrField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class CheckedAccessories + { + + private string accCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string AccCode + { + get + { + return this.accCodeField; + } + set + { + this.accCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetAuctionValuesRequest : NADAValueRequest + { + + private int uidField; + + private string vicField; + + private int regionField; + + private int mileageField; + + private string customerIdField; + + private int userIdField; + + private string callingApplicationField; + + private CheckedAccessories[] checkedAccessoriesListField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string Vic + { + get + { + return this.vicField; + } + set + { + this.vicField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public int Mileage + { + get + { + return this.mileageField; + } + set + { + this.mileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=4)] + public string CustomerId + { + get + { + return this.customerIdField; + } + set + { + this.customerIdField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=5)] + public int UserId + { + get + { + return this.userIdField; + } + set + { + this.userIdField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=6)] + public string CallingApplication + { + get + { + return this.callingApplicationField; + } + set + { + this.callingApplicationField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayAttribute(Order=7)] + public CheckedAccessories[] CheckedAccessoriesList + { + get + { + return this.checkedAccessoriesListField; + } + set + { + this.checkedAccessoriesListField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehicleValuesByMsrpRequest : NADAValueRequest + { + + private string vinField; + + private int regionField; + + private int mileageField; + + private int msrpField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int Mileage + { + get + { + return this.mileageField; + } + set + { + this.mileageField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=3)] + public int Msrp + { + get + { + return this.msrpField; + } + set + { + this.msrpField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehicleValuesByVinRequest : NADAValueRequest + { + + private string vinField; + + private int regionField; + + private int mileageField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int Mileage + { + get + { + return this.mileageField; + } + set + { + this.mileageField = value; + } + } + } + + /// + [System.Xml.Serialization.XmlIncludeAttribute(typeof(GetAdjustedValuesByUidRequest))] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehicleValuesByUidRequest : NADAValueRequest + { + + private int uidField; + + private int regionField; + + private int mileageField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int Mileage + { + get + { + return this.mileageField; + } + set + { + this.mileageField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetAdjustedValuesByUidRequest : GetVehicleValuesByUidRequest + { + + private string[] accessoryListField; + + private string vinField; + + /// + [System.Xml.Serialization.XmlArrayAttribute(Order=0)] + public string[] AccessoryList + { + get + { + return this.accessoryListField; + } + set + { + this.accessoryListField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetRegionByStateCodeRequest : NADAValueRequest + { + + private string stateCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string StateCode + { + get + { + return this.stateCodeField; + } + set + { + this.stateCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetRegionsRequest : NADAValueRequest + { + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetMileageAdjByUidRequest : NADAValueRequest + { + + private int uidField; + + private int regionField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class ValidateVinRequest : NADAValueRequest + { + + private string vINField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string VIN + { + get + { + return this.vINField; + } + set + { + this.vINField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetMutualAccessoriesRequest : NADAValueRequest + { + + private int uidField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetAccessoriesRequest : NADAValueRequest + { + + private int uidField; + + private string vinField; + + private int regionField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int Region + { + get + { + return this.regionField; + } + set + { + this.regionField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehicleByVicRequest : NADAValueRequest + { + + private string vicField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Vic + { + get + { + return this.vicField; + } + set + { + this.vicField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehicleByUidRequest : NADAValueRequest + { + + private int uidField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Uid + { + get + { + return this.uidField; + } + set + { + this.uidField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetVehiclesByVinRequest : NADAValueRequest + { + + private string vinField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public string Vin + { + get + { + return this.vinField; + } + set + { + this.vinField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetBodyUidsRequest : NADAValueRequest + { + + private int yearField; + + private int makeCodeField; + + private int seriesCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Year + { + get + { + return this.yearField; + } + set + { + this.yearField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int MakeCode + { + get + { + return this.makeCodeField; + } + set + { + this.makeCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=2)] + public int SeriesCode + { + get + { + return this.seriesCodeField; + } + set + { + this.seriesCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetSeriesRequest : NADAValueRequest + { + + private int yearField; + + private int makeCodeField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Year + { + get + { + return this.yearField; + } + set + { + this.yearField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=1)] + public int MakeCode + { + get + { + return this.makeCodeField; + } + set + { + this.makeCodeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://webservice.jdpowervalues.com/")] + public partial class GetMakesRequest : NADAValueRequest + { + + private int yearField; + + /// + [System.Xml.Serialization.XmlElementAttribute(Order=0)] + public int Year + { + get + { + return this.yearField; + } + set + { + this.yearField = value; + } + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + public interface VehicleSoapChannel : JdpVehicle.VehicleSoap, System.ServiceModel.IClientChannel + { + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")] + public partial class VehicleSoapClient : System.ServiceModel.ClientBase, JdpVehicle.VehicleSoap + { + + /// + /// Implement this partial method to configure the service endpoint. + /// + /// The endpoint to configure + /// The client credentials + static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials); + + public VehicleSoapClient(EndpointConfiguration endpointConfiguration) : + base(VehicleSoapClient.GetBindingForEndpoint(endpointConfiguration), VehicleSoapClient.GetEndpointAddress(endpointConfiguration)) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public VehicleSoapClient(EndpointConfiguration endpointConfiguration, string remoteAddress) : + base(VehicleSoapClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress)) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public VehicleSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : + base(VehicleSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress) + { + this.Endpoint.Name = endpointConfiguration.ToString(); + ConfigureEndpoint(this.Endpoint, this.ClientCredentials); + } + + public VehicleSoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : + base(binding, remoteAddress) + { + } + + public System.Threading.Tasks.Task pingAsync(string token) + { + return base.Channel.pingAsync(token); + } + + public System.Threading.Tasks.Task getYearsAsync(JdpVehicle.GetYearsRequest l_Request) + { + return base.Channel.getYearsAsync(l_Request); + } + + public System.Threading.Tasks.Task getMakesAsync(JdpVehicle.GetMakesRequest l_Request) + { + return base.Channel.getMakesAsync(l_Request); + } + + public System.Threading.Tasks.Task getSeriesAsync(JdpVehicle.GetSeriesRequest l_Request) + { + return base.Channel.getSeriesAsync(l_Request); + } + + public System.Threading.Tasks.Task getBodyUidsAsync(JdpVehicle.GetBodyUidsRequest l_Request) + { + return base.Channel.getBodyUidsAsync(l_Request); + } + + public System.Threading.Tasks.Task getVehiclesAsync(JdpVehicle.GetVehiclesByVinRequest vehicleRequest) + { + return base.Channel.getVehiclesAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getVehicleAsync(JdpVehicle.GetVehicleByUidRequest vehicleRequest) + { + return base.Channel.getVehicleAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getVehicleByVicAsync(JdpVehicle.GetVehicleByVicRequest vehicleRequest) + { + return base.Channel.getVehicleByVicAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getAccessoriesAsync(JdpVehicle.GetAccessoriesRequest accessoriesRequest) + { + return base.Channel.getAccessoriesAsync(accessoriesRequest); + } + + public System.Threading.Tasks.Task getInclusiveAccessoriesAsync(JdpVehicle.GetMutualAccessoriesRequest mutualAccessoriesRequest) + { + return base.Channel.getInclusiveAccessoriesAsync(mutualAccessoriesRequest); + } + + public System.Threading.Tasks.Task getExclusiveAccessoriesAsync(JdpVehicle.GetMutualAccessoriesRequest mutualAccessoriesRequest) + { + return base.Channel.getExclusiveAccessoriesAsync(mutualAccessoriesRequest); + } + + public System.Threading.Tasks.Task validateVinAsync(JdpVehicle.ValidateVinRequest l_request) + { + return base.Channel.validateVinAsync(l_request); + } + + public System.Threading.Tasks.Task getMileageAdjAsync(JdpVehicle.GetMileageAdjByUidRequest mileageAdjRequest) + { + return base.Channel.getMileageAdjAsync(mileageAdjRequest); + } + + public System.Threading.Tasks.Task getRegionsAsync(JdpVehicle.GetRegionsRequest l_Request) + { + return base.Channel.getRegionsAsync(l_Request); + } + + public System.Threading.Tasks.Task getRegionByStateCodeAsync(JdpVehicle.GetRegionByStateCodeRequest l_Request) + { + return base.Channel.getRegionByStateCodeAsync(l_Request); + } + + public System.Threading.Tasks.Task getTotalAdjFloorValuesAsync(JdpVehicle.GetVehicleValuesByUidRequest l_Request) + { + return base.Channel.getTotalAdjFloorValuesAsync(l_Request); + } + + public System.Threading.Tasks.Task getVehicleValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest) + { + return base.Channel.getVehicleValueByUidAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getBaseVehicleValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest) + { + return base.Channel.getBaseVehicleValueByUidAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getVehicleAndValueByUidAsync(JdpVehicle.GetVehicleValuesByUidRequest vehicleRequest) + { + return base.Channel.getVehicleAndValueByUidAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getDefaultVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest) + { + return base.Channel.getDefaultVehicleAndValueByVinAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getMsrpVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByMsrpRequest vehicleRequest) + { + return base.Channel.getMsrpVehicleAndValueByVinAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getHighVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest) + { + return base.Channel.getHighVehicleAndValueByVinAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getLowVehicleAndValueByVinAsync(JdpVehicle.GetVehicleValuesByVinRequest vehicleRequest) + { + return base.Channel.getLowVehicleAndValueByVinAsync(vehicleRequest); + } + + public System.Threading.Tasks.Task getAdjustedValuesByUidAsync(JdpVehicle.GetAdjustedValuesByUidRequest request) + { + return base.Channel.getAdjustedValuesByUidAsync(request); + } + + public System.Threading.Tasks.Task getAuctionValuesAsync(JdpVehicle.GetAuctionValuesRequest auctionValuesRequest) + { + return base.Channel.getAuctionValuesAsync(auctionValuesRequest); + } + + public System.Threading.Tasks.Task getExperianAutoCheckReportAsync(JdpVehicle.GetExperianAutoCheckRequest experianAutocheckRequest) + { + return base.Channel.getExperianAutoCheckReportAsync(experianAutocheckRequest); + } + + public virtual System.Threading.Tasks.Task OpenAsync() + { + return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndOpen)); + } + + public virtual System.Threading.Tasks.Task CloseAsync() + { + return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginClose(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndClose)); + } + + private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration) + { + if ((endpointConfiguration == EndpointConfiguration.VehicleSoap)) + { + System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding(); + result.MaxBufferSize = int.MaxValue; + result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max; + result.MaxReceivedMessageSize = int.MaxValue; + result.AllowCookies = true; + return result; + } + if ((endpointConfiguration == EndpointConfiguration.VehicleSoap12)) + { + System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding(); + System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement(); + textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None); + result.Elements.Add(textBindingElement); + System.ServiceModel.Channels.HttpTransportBindingElement httpBindingElement = new System.ServiceModel.Channels.HttpTransportBindingElement(); + httpBindingElement.AllowCookies = true; + httpBindingElement.MaxBufferSize = int.MaxValue; + httpBindingElement.MaxReceivedMessageSize = int.MaxValue; + result.Elements.Add(httpBindingElement); + return result; + } + throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration)); + } + + private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration) + { + if ((endpointConfiguration == EndpointConfiguration.VehicleSoap)) + { + return new System.ServiceModel.EndpointAddress("http://webservice-stage.jdpowervalues.com/vehicles/vehicle.asmx"); + } + if ((endpointConfiguration == EndpointConfiguration.VehicleSoap12)) + { + return new System.ServiceModel.EndpointAddress("http://webservice-stage.jdpowervalues.com/vehicles/vehicle.asmx"); + } + throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration)); + } + + public enum EndpointConfiguration + { + + VehicleSoap, + + VehicleSoap12, + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/vehicle.wsdl b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/vehicle.wsdl new file mode 100644 index 0000000..5e02c4f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Connected Services/JdpVehicle/vehicle.wsdl @@ -0,0 +1,1597 @@ + + + This is JDPA Web Service into its vehicle evaluation data. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Returns the boolean value true if the service is functioning and the token is valid. + + + + + Returns the valid years for the inputted parameters. + + + + + Returns the valid makes for the inputted parameters. + + + + + Returns the valid series for the inputted parameters. + + + + + Returns the valid bodies for the inputted parameters. + + + + + Returns an array of vehicles associated with the inputted parameters. + + + + + Returns a vehicle associated with the inputted parameters. + + + + + Returns a vehicle associated with the inputted parameters. + + + + + Returns the accessories associated with the inputted parameters. + + + + + Returns the packaged accessories associated with the inputted parameters. + + + + + Returns the mutually exclusive accessories associated with the inputted parameters. + + + + + Returns true if the inputted vin is valid and false if it is not. + + + + + Returns the mileage adjustments associated with the inputted parameters. + + + + + Returns the valid regions for the inputted parameters. + + + + + Returns the valid region id for the inputted two letter state code. + + + + + Returns the minimum total adjusted value for the inputted parameters. + + + + + Returns the vehicle values associated with the inputted parameters. + + + + + Returns the base vehicle values associated with the inputted parameters. + + + + + Returns the vehicle value for the default vehicle associated with the inputted parameters. + + + + + Returns the vehicle value for the default vehicle associated with the inputted parameters. + + + + + Returns the value for vehicle associated with the inputted VIN and MSRP. + + + + + Returns the vehicle value for highest vehicle value associated with the inputted parameters. + + + + + Returns the vehicle value for lowest vehicle value associated with the inputted parameters. + + + + + Returns the adjusted vehicle values associated with the inputted parameters + + + + + Returns the auction values associated with the inputted parameters. + + + + + Returns Experian AutoCheck reports with the inputted parameters. Requires Experian AutoCheck credentials. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is JDPA Web Service into its vehicle evaluation data. + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/DuplicateResourceException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/DuplicateResourceException.cs new file mode 100644 index 0000000..af73cba --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/DuplicateResourceException.cs @@ -0,0 +1,9 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Exceptions +{ + public class DuplicateResourceException : Exception + { + public DuplicateResourceException(string resourceType) : base($"Duplicate Resources of type {resourceType} were found.") { } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/MaxItemException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/MaxItemException.cs new file mode 100644 index 0000000..6053873 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/MaxItemException.cs @@ -0,0 +1,13 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Exceptions +{ + public class MaxItemException : Exception + { + public MaxItemException(int itemCount) : base("Too Many Items Were Produced Than Were Allowed.") { + ItemCount = itemCount; + } + + public int ItemCount { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/UnwantedStatusException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/UnwantedStatusException.cs new file mode 100644 index 0000000..fc56d12 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Exceptions/UnwantedStatusException.cs @@ -0,0 +1,9 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Exceptions +{ + public class UnwantedStatusException : Exception + { + public UnwantedStatusException(int repoId, string accountNo) : base($"Account Number {accountNo} with Repossession ID {repoId} is in Cancelled or Redeemed status") { } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Extensions/QueryableIncludeAll.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Extensions/QueryableIncludeAll.cs new file mode 100644 index 0000000..4b20c75 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Extensions/QueryableIncludeAll.cs @@ -0,0 +1,37 @@ +using System.Linq; +using LCU.API.Internal.RepoApi.Repos.Models; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Extensions +{ + public static class QueryableIncludeAll + { + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable + .Include(repo => repo.Borrowers) + .Include(repo => repo.Collaterals) + .ThenInclude(collateral => collateral.RepoCompanySelections) + .Include(repo => repo.RepoStatusChanges) + .Include(repo => repo.RefundStatus) + .Include(repo => repo.SixtyPlusCollector) + ; + } + + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable + .Include(auctionAssignment => auctionAssignment.Auction) + .Include(auctionAssignment => auctionAssignment.AutoIMS) + .Include(auctionAssignment => auctionAssignment.FloorPriceValuesUsed) + .ThenInclude(fpvu => fpvu.CollateralValuation) + .Include(auctionAssignment => auctionAssignment.AuctionAssignmentToCollaterals) + .ThenInclude(aa2c => aa2c.Collateral); + } + + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable.Include(collateral => collateral.RepoCompanySelections); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/LCU.API.Internal.RepoApi.Repos.csproj b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/LCU.API.Internal.RepoApi.Repos.csproj new file mode 100644 index 0000000..c28a68f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/LCU.API.Internal.RepoApi.Repos.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignment.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignment.cs new file mode 100644 index 0000000..41e93e5 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignment.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class AuctionAssignment + { + [Required] + public int ID { get; set; } + [Required] + public string AuctionId { get; set; } + [Required] + public int? RepoId { get; set; } + public string AuctionCustomerNo { get; set; } + public string CollateralVIN { get; set; } + public DateTime? DateSubmitted { get; set; } + public decimal? FloorPrice { get; set; } + public int? FloorPriceResponseCode { get; set; } + public string FloorPriceResponseDescription { get; set; } + public string FloorPriceSubmissionErrors { get; set; } + public int? HoldStatus { get; set; } + public DateTime? HoldUntil { get; set; } + public DateTime? RedeemedDate { get; set; } + public int? VehicleType { get; set; } + public string BodyStyle { get; set; } + public bool? Inoperable { get; set; } + public decimal? RetailValue { get; set; } + public string AuctionNotes { get; set; } + public DateTime? AvailablePickupDate { get; set; } + public DateTime? LegalSaleDate { get; set; } + public int? CleaningLevel { get; set; } + public int? SubmissionResponseCode { get; set; } + public string SubmissionResponseDescription { get; set; } + public string SubmissionErrors { get; set; } + public DateTime? SentTitleDocsDate { get; set; } + public int? TitleSentResponseCode { get; set; } + public string TitleSentResponseDescription { get; set; } + public string TitleSentSubmissionErrors { get; set; } + public bool? AddedThroughRepoDatabase { get; set; } + public bool IsActive { get; set; } + + public ImsAutoFileData AutoIMS { get; set; } + public AuctionHouse Auction { get; set; } + public IEnumerable AuctionAssignmentToCollaterals { get; set; } + public FloorPriceValuesUsed FloorPriceValuesUsed { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentNote.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentNote.cs new file mode 100644 index 0000000..6908e98 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentNote.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class AuctionAssignmentNote + { + [Required] + public int Id { get; set; } + [Required] + public int AuctionAssignmentId { get; set; } + [Required] + public string AuctionCustomerNo { get; set; } + public string Origin { get; set; } + public string Writer { get; set; } + public string Type { get; set; } + public string Text { get; set; } + public DateTime? DateWritten { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentToCollateral.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentToCollateral.cs new file mode 100644 index 0000000..9047fbd --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionAssignmentToCollateral.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class AuctionAssignmentToCollateral + { + [Key] + public int AuctionAssignmentId { get; set; } + [Key] + public int CollateralId { get; set; } + public AuctionAssignment AuctionAssignment { get; set; } + public Collateral Collateral { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionHouse.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionHouse.cs new file mode 100644 index 0000000..f8b1f34 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuctionHouse.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class AuctionHouse + { + [Required] + public string AuctionID { get; set; } + [Required] + public string Name { get; set; } + public string Active { get; set; } + public string Address_Line_1 { get; set; } + public string Address_Line_2 { get; set; } + public string City { get; set; } + public string State { get; set; } + public string ZIP { get; set; } + public string Country { get; set; } + public string Phone { get; set; } + public string Fax { get; set; } + public decimal? Latitude { get; set; } + public decimal? Longitude { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuditRecord.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuditRecord.cs new file mode 100644 index 0000000..180cb12 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AuditRecord.cs @@ -0,0 +1,18 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class AuditRecord + { + public int ID { get; set; } + // Name of object modified + public string ClassName { get; set; } + // category of modification + public string Action { get; set; } + public string Description { get; set; } + public string PerformedBy { get; set; } + public System.DateTime DatePerformed { get; set; } + // The ID representation of the object modified + public string KeyRepresentation { get; set; } + // Don't want to use, but this is a json of the whole object currently + public string PropertyValues { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/FtpConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/FtpConfiguration.cs new file mode 100644 index 0000000..4d68f0c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/FtpConfiguration.cs @@ -0,0 +1,13 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.AutoIms +{ + public class FtpConfiguration + { + public string HostName { get; set; } + public string UserName { get; set; } + public int Port { get; set; } + public string ExecutablePath { get; set; } + public string SshHostKeyFingerprint { get; set; } + public string SshPrivateKeyPath { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImportResultRow.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImportResultRow.cs new file mode 100644 index 0000000..27110a1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImportResultRow.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Internal; + +namespace LCU.API.Internal.RepoApi.Repos.Models.AutoIms +{ + public class ImportResultRow + { + public bool Success { get; set; } + public List Messages { get; set; } + public string Description { get; set; } + public ImsAutoFileData Data { get; set; } + public RepoAccount RepoAccount { get; set; } + + public string BuildStatusReport(string repoManagerUrl) + { + var report = new List(); + var success = Success ? "Success" : "Failure"; + if (RepoAccount != null) + { + report.Add(CreateRepoLink(RepoAccount, repoManagerUrl)); + } + if(RepoAccount == null && Data != null) + { + report.Add($"Auto IMS Update for {Data.VIN}, no Repo Account."); + } + report.Add($"{success} : {Description}"); + if (Messages != null && Messages.Any()) + { + report.AddRange(Messages); + } + + return report.ToString(); + } + + public static string CreateRepoLink(RepoAccount account, string repoManagerUrl) + { + var url = repoManagerUrl + account.RepoId; + var link = string.Format("{1}", url, $"Account {account.RepoId} ({account.AccountNo})") ; + return link; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImsAutoFileImportData.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImsAutoFileImportData.cs new file mode 100644 index 0000000..cc53c52 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/AutoIms/ImsAutoFileImportData.cs @@ -0,0 +1,71 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.AutoIms +{ + /** + * This models the rows in the CSV File so that they can be different from the rows in SQL + */ + public class ImsAutoFileImportData + { + public string AnnouncedConditions { get; set; } + public string ArbitrationReason { get; set; } + public string AsIs100KMiles { get; set; } + public string AssignmentAccepted { get; set; } + public string AuctionAge { get; set; } + public string AuctionGuideBookPrice { get; set; } + public string BuyerID { get; set; } + public string BuyerName { get; set; } + public string ChargeAmount { get; set; } + public string CheckAmount { get; set; } + public string ClientAge { get; set; } + public string ClientCancelled { get; set; } + public string CollisionDamage { get; set; } + public string InspectedDate { get; set; } + public string DateofLoss { get; set; } + public string DatePickedUp { get; set; } + public string DateEnteredAutoIMS { get; set; } + public string DaysRun { get; set; } + public string DaysRunHighestBidRecordedbyAuction { get; set; } + public string DaystoDelivery { get; set; } + public string Description { get; set; } + public string Drivable { get; set; } + public string FloorPriceAccepted { get; set; } + public string FundsSent { get; set; } + public string GrossPercentFloor { get; set; } + public string HighestBid { get; set; } + public string StatedMileage { get; set; } + public string NetPercentFloor { get; set; } + public string OtherAdvanceFees { get; set; } + public string OTHERTotal { get; set; } + public string OTHERAdvancedOther { get; set; } + public string OTHERAdvancedStorage { get; set; } + public string OTHERAdvancedTow { get; set; } + public string OTHERMiscellaneous { get; set; } + public string SalePrice { get; set; } + public string ScheduledSaleDate { get; set; } + public string SoldDate { get; set; } + public string TitleNumber { get; set; } + public string TitleReceived { get; set; } + public string TitleReturned { get; set; } + public string TitleState { get; set; } + public string TitleStatus { get; set; } + public string TitleStatusfromAuction { get; set; } + public string TotalCharges { get; set; } + public string Operable { get; set; } + public string ShipmentDate { get; set; } + public string Status { get; set; } + public string VIN { get; set; } + public string StorageCost { get; set; } + public string StorageRatePerDay { get; set; } + public string TowCost { get; set; } + public string CustomerAccountNumber { get; set; } + public string DateReceivedFromAutoIMS { get; set; } + public string VehicleDecimalGradeCode { get; set; } + public string VehicleGrade { get; set; } + public string VehicleGradeApprovedCR { get; set; } + public string MMRNationalAdjustedPrice { get; set; } + public string MMRRegionalAdjustedPrice { get; set; } + public string MMR { get; set; } + public string ConditionReportLink { get; set; } + public string CollateralImagePath { get; set; } + public string AuctionAssignmentID { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Borrower.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Borrower.cs new file mode 100644 index 0000000..34c5902 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Borrower.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class Borrower + { + [Required] + public int RepoId { get; set; } + [Required] + public string IndividualId { get; set; } + [Required] + public int OwnershipType { get; set; } + [Required] + public string IndividualName { get; set; } + + [ForeignKey("RepoId")] + [JsonIgnore] + public RepoAccount RepoAccount { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ClientError.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ClientError.cs new file mode 100644 index 0000000..18e395b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ClientError.cs @@ -0,0 +1,10 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class ClientError + { + public string Message { get; set; } + public string Name { get; set; } + public string StackTrace { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Collateral.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Collateral.cs new file mode 100644 index 0000000..cd9e653 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Collateral.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class Collateral + { + [Required] + public int CollateralId { get; set; } + [Required] + public int RepoId { get; set; } + public string? Vin { get; set; } + public string? Make { get; set; } + public string? Model { get; set; } + public int? Year { get; set; } + public string? Description { get; set; } + public int? VehicleType { get; set; } + public string? BodyStyle { get; set; } + public bool? Inoperable { get; set; } + public int? CollateralTypeId { get; set; } + public DateTime? RetitledInLandmarkName { get; set; } + public string? RefCollateralId { get; set; } + + [ForeignKey("CollateralId")] + public IReadOnlyCollection RepoCompanySelections { get; set; } + + [ForeignKey("RepoId")] + [JsonIgnore] + public RepoAccount RepoAccount { get; set; } + + [JsonIgnore] + public IEnumerable AuctionAssignmentToCollaterals { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AddressDetail.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AddressDetail.cs new file mode 100644 index 0000000..4ad0b05 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AddressDetail.cs @@ -0,0 +1,35 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class AddressDetail + { + public int AddressId { get; set; } + public string Line1 { get; set; } + public string Line2 { get; set; } + public string Line3 { get; set; } + public string City { get; set; } + public string State { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + public string CountyName { get; set; } + public string AddressType { get; set; } + public bool IsPrimary { get; set; } + public bool IsInternational { get; set; } + public int AddressVerification { get; set; } + public string Occupancy { get; set; } + public System.DateTime StartDate { get; set; } + public System.DateTime EndDate { get; set; } + public string LongAddress1 { get; set; } + public string LongAddress2 { get; set; } + public string[] Membership { get; set; } + public string[] CoBorrower { get; set; } + public System.DateTime UseAlternateAsOf { get; set; } + public System.DateTime RestorePrimaryAsOf { get; set; } + public string AlternateType { get; set; } + public string Comments { get; set; } + public string Met2AddressIndicator { get; set; } + public bool UspsVerified { get; set; } + public string AddressMatchStatus { get; set; } + public bool IsHSA { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AppraisalDetail.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AppraisalDetail.cs new file mode 100644 index 0000000..ee783e3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/AppraisalDetail.cs @@ -0,0 +1,25 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class AppraisalDetail + { + public decimal OriginalAppraisedValue { get; set; } + public string OriginalAppraisedBy { get; set; } + public string OriginalAppraisalType { get; set; } + public System.DateTime OriginalAppraisalDate { get; set; } + public decimal LastAppraisedValue { get; set; } + public string LastAppraisedBy { get; set; } + public string LastAppraisalType { get; set; } + public System.DateTime LastAppraisalDate { get; set; } + public bool OriginalAppraisalProvided { get; set; } + public System.DateTime OriginalDateProvided { get; set; } + public int OriginalProvidedByOperatorID { get; set; } + public string OriginalProvidedByOperatorName { get; set; } + public string OriginalDeliveryMethod { get; set; } + public bool LastAppraisalProvided { get; set; } + public System.DateTime LastDateProvided { get; set; } + public int LastProvidedByOperatorID { get; set; } + public string LastProvidedByOperatorName { get; set; } + public string LastDeliveryMethod { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CollateralDetail.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CollateralDetail.cs new file mode 100644 index 0000000..564ede6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CollateralDetail.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class CollateralDetail + { + public int CollateralId { get; set; } + public int OwnerId { get; set; } + public string OwnerName { get; set; } + public string CollateralType { get; set; } + public string NameOnTitle { get; set; } + public string Description { get; set; } + public decimal PurchasePrice { get; set; } + public decimal CurrentValue { get; set; } + public string SourceOfEvaluation { get; set; } + public System.DateTime EvaluationDate { get; set; } + public int ValueVerifiedByOperatorNumber { get; set; } + public string ValueVerifiedByOperatorName { get; set; } + public string VerificationMethod { get; set; } + public System.DateTime VerificationDate { get; set; } + public int VerifiedBy { get; set; } + public string SerialNumber { get; set; } + public Vehicle Vehicle { get; set; } + public OtherCollateral Other { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CoreTransaction.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CoreTransaction.cs new file mode 100644 index 0000000..6d23a65 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/CoreTransaction.cs @@ -0,0 +1,16 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + // NOTE: This currently does not contain all possible fields. + // Instead, it only contains the fields necessary. + // NOTE NOTE: This class will change heavily. + public class CoreTransaction + { + public decimal? runningBalance { get; set; } + public DateTime? postingDate { get; set; } + public decimal? amount { get; set; } + public string transactionTypeCode { get; set; } + public string transactionTypeDescription { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanDetail.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanDetail.cs new file mode 100644 index 0000000..a68e88e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanDetail.cs @@ -0,0 +1,268 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class LoanDetail + { + public long MemberId { get; set; } + public long LoanId { get; set; } + public long ExternalLoanId { get; set; } + public int[] IndividualId { get; set; } + public List Borrowers { get; set; } + public string LoanType { get; set; } + public string Title { get; set; } + public string Alerts { get; set; } + public string Product { get; set; } + public int ProductCode { get; set; } + public string Template { get; set; } + public int ProductId { get; set; } + public string ProductDescription { get; set; } + public string ProductCategory { get; set; } + public string ProductClass { get; set; } + public int AccountType { get; set; } + public string Status { get; set; } + public System.DateTime ClosedDate { get; set; } + public string Category { get; set; } + public string Classification { get; set; } + public string MetroAcctType { get; set; } + public string SMSA { get; set; } + public string Officer { get; set; } + public string OtherSecurity { get; set; } + public string NoteNumber { get; set; } + public System.DateTime LastMaintenance { get; set; } + public System.DateTime WrittenOffDate { get; set; } + public string CreditCardNumber { get; set; } + public System.DateTime LastCycleDate { get; set; } + public string LegalDescription { get; set; } + public string PropertyType { get; set; } + public string PurposeCode { get; set; } + public string CollateralCode { get; set; } + public string RefinanceCount { get; set; } + public System.DateTime LastRefinanceDate { get; set; } + public string LdcNumber { get; set; } + public string Messages { get; set; } + public decimal OriginalAmount { get; set; } + public System.DateTime OriginationDate { get; set; } + public decimal Balance { get; set; } + public decimal AvailableBalance { get; set; } + public decimal Limit { get; set; } + public decimal EscrowBalance { get; set; } + public decimal Payoff { get; set; } + public decimal UnappliedFundsBalance { get; set; } + public decimal CashAdvanceBalance { get; set; } + public decimal PurchaseAdvanceBalance { get; set; } + public decimal ActiveAuthorizationAmount { get; set; } + public decimal EscrowDividends { get; set; } + public string PledgeType { get; set; } + public string MiscCharge1 { get; set; } + public string MiscCharge2 { get; set; } + public string MiscCharge3 { get; set; } + public string MiscChargeCode1 { get; set; } + public string MiscChargeCode2 { get; set; } + public string MiscChargeCode3 { get; set; } + public decimal InterestMemoed { get; set; } + public System.DateTime InterestAccruedDate { get; set; } + public decimal PaymentInterestDue { get; set; } + public decimal PayoffInterestDue { get; set; } + public string LateChargeMethod { get; set; } + public decimal LateCharge { get; set; } + public System.DateTime LateChargeDate { get; set; } + public decimal CashAdvance { get; set; } + public string Insurance { get; set; } + public decimal LastStatementLateCharges { get; set; } + public decimal LastStatementMiscFees { get; set; } + public decimal LastStatementPurchaseAdvances { get; set; } + public decimal LastStatementCashAdvances { get; set; } + public decimal LastStatementCashAdvanceFees { get; set; } + public decimal LastStatementMiscDebits { get; set; } + public decimal LastStatementTotalPayments { get; set; } + public decimal LastStatementCreditsPosted { get; set; } + public decimal LastStatementAccruedInterest { get; set; } + public System.DateTime LastStatementDate { get; set; } + public decimal LastStatementPurchaseAdvanceBalance { get; set; } + public decimal LastStatementEndingBalance { get; set; } + public decimal DelinquentAmount { get; set; } + public decimal LastStatementMonthlyMinimum { get; set; } + public decimal LastStatementRequiredDue { get; set; } + public decimal LastStatementRequiredPayment { get; set; } + public decimal ScheduledPayment { get; set; } + public System.DateTime NextDueDate { get; set; } + public decimal PartialPaymentAmount { get; set; } + public System.DateTime PreviousDate { get; set; } + public string PaymentFrequency { get; set; } + public System.DateTime AlignmentDate { get; set; } + public string PaymentMethod { get; set; } + public string SourceAccount { get; set; } + public string ProcessingGroup { get; set; } + public System.DateTime LoanCouponDate { get; set; } + public decimal RequiredPaymentDue { get; set; } + public System.DateTime FirstPaymentDate { get; set; } + public decimal AlternatePaymentAmount { get; set; } + public System.DateTime AlternateBeginDate { get; set; } + public System.DateTime AlternateEndDate { get; set; } + public int PaymentSkips { get; set; } + public decimal FinalPaymentAmount { get; set; } + public System.DateTime FinalPaymentDate { get; set; } + public decimal CycleAmount { get; set; } + public string VariableRatePaymentChange { get; set; } + public string VariableRateCode { get; set; } + public decimal MinimumDue { get; set; } + public decimal FrequencyPayment { get; set; } + public decimal EscrowPayment { get; set; } + public decimal PrincipalAndInterestPayment { get; set; } + public string PaymentLoanDefinitionNumber { get; set; } + public string RateLoanDefinitionNumber { get; set; } + public int VacationWeeks { get; set; } + public int PayrollRepaidWeeklyPaymentCount { get; set; } + public decimal BaseRate { get; set; } + public decimal TieredRateAdj { get; set; } + public decimal ManualRateAdj { get; set; } + public decimal Rate { get; set; } + public decimal ManualRate { get; set; } + public decimal APR { get; set; } + public decimal OriginalInterestRate { get; set; } + public decimal AlternateInterestRate { get; set; } + public System.DateTime LastRateChange { get; set; } + public decimal RealEstateMargin { get; set; } + public string Amortization { get; set; } + public int AmortizationTerm { get; set; } + public int MonthlyTerm { get; set; } + public int NumberOfPayments { get; set; } + public decimal InterestYTD { get; set; } + public decimal InterestPYTD { get; set; } + public decimal InterestDaily { get; set; } + public decimal PrincipalYTD { get; set; } + public decimal PrincipalPYTD { get; set; } + public decimal LateChargeYTD { get; set; } + public decimal LateChargePYTD { get; set; } + public string PaymentHistory { get; set; } + public System.DateTime LastPaymentDue { get; set; } + public int PaymentsPastDue { get; set; } + public int DaysPastDue { get; set; } + public int MonthsPastDue { get; set; } + public decimal CurrentStatementLateCharges { get; set; } + public decimal CurrentStatementMiscFees { get; set; } + public decimal CurrentStatementPurchaseAdvances { get; set; } + public decimal CurrentStatementCashAdvances { get; set; } + public decimal CurrentStatementCashAdvanceFees { get; set; } + public decimal CurrentStatementMiscDebits { get; set; } + public decimal CurrentStatementTotalPayments { get; set; } + public decimal CurrentStatementTotalCredits { get; set; } + public System.DateTime DelinquentPaymentDueDate { get; set; } + public bool IsInactive { get; set; } + public string StatementFrequency { get; set; } + public decimal AppraisedValue { get; set; } + public int LocCycleDay { get; set; } + public bool IgnorePledge { get; set; } + public bool OpenEndPlan { get; set; } + public string OpenEndCategory { get; set; } + public decimal PointsPaid { get; set; } + public decimal CurtailmentAmount { get; set; } + public System.DateTime CurtailmentDate { get; set; } + public System.DateTime InsuranceExpirationDate { get; set; } + public string LifeInsuranceMI { get; set; } + public string DisabilityInsuranceMI { get; set; } + public string LifeActivityInsuranceCode { get; set; } + public string DisabilityActivityInsuranceCode { get; set; } + public bool LoanCoupons { get; set; } + public bool PaidbyGrace { get; set; } + public string EscrowAccount { get; set; } + public bool EscrowInterestPaid { get; set; } + public System.DateTime NextPaymentAdjustmentDate { get; set; } + public decimal EffectivePayment { get; set; } + + public decimal InterestRate { get; set; } + + public System.DateTime LastPaymentDate { get; set; } + public System.DateTime LastPurchaseDate { get; set; } + public System.DateTime LastCashAdvanceDate { get; set; } + public string BlockCode { get; set; } + public string ReclassCode { get; set; } + public int OutstandingAuthCount { get; set; } + public decimal OutstandingAuthAmount { get; set; } + public string InterestType { get; set; } + public string AlternateLoanNumber { get; set; } + public int ArmIndex { get; set; } + public string DeductionOption { get; set; } + public bool DelinquentPaymentSkips { get; set; } + public bool FirstPayment { get; set; } + public decimal InterestDue { get; set; } + public bool LoanLevelSkips { get; set; } + public bool PaymentReduction { get; set; } + public bool WaiveLateCharge { get; set; } + public int ExcessPaymentTargetAccountId { get; set; } + public long ExcessPaymentMemberId { get; set; } + public bool CustRate { get; set; } + public string IntRateMethod { get; set; } + public decimal Margin { get; set; } + public string Index { get; set; } + public decimal RndFactPct { get; set; } + public string RndFactOpt { get; set; } + public decimal IntRateCeiling { get; set; } + public decimal IntRateFloor { get; set; } + public decimal MaxDnRateChgLife { get; set; } + public decimal MaxUpRateChgLife { get; set; } + public decimal MaxDnRateChgPeriod { get; set; } + public decimal MaxUpRateChgPeriod { get; set; } + public decimal MinDnRateChgPeriod { get; set; } + public decimal MinUpRateChgPeriod { get; set; } + public short PmtAdjPeriod { get; set; } + public short DaysPriorToPeriodicRateAdj { get; set; } + public short DaysPriorToInitialRateAdjustment { get; set; } + public bool FirstAdjustmentIsEstimate { get; set; } + public System.DateTime InitialAnalysisEstimateDate { get; set; } + public string EstimatePerformed { get; set; } + + public int BalloonTerm { get; set; } + + public string PledgeTypeDescription { get; set; } + + public string SystemApproved { get; set; } + + public string[] VINList { get; set; } + + public long? MortgageLoanOriginator { get; set; } + public decimal? LastPaymentPrincipal { get; set; } + public decimal? LastPaymentInterest { get; set; } + public decimal? LastPaymentFees { get; set; } + public decimal? LastPaymentLateCharges { get; set; } + public decimal? LastPaymentEscrowImpound { get; set; } + public decimal? LastPaymentCurtailment { get; set; } + public decimal? LastPaymentTotal { get; set; } + public short TransferDay { get; set; } + + public long SourceMember { get; set; } + + public bool AdjustPurchaseAccrual { get; set; } + public bool AnnualFee { get; set; } + public System.DateTime AnnualFeeDate { get; set; } + public decimal BalaceLimit { get; set; } + public bool CloseOnZero { get; set; } + public System.DateTime DrawDate { get; set; } + public string Form1098Option { get; set; } + public bool MethodADelinquency { get; set; } + public short? NumberofSkips { get; set; } + public decimal OverlimitAmount { get; set; } + public System.DateTime OverlimitDate { get; set; } + public decimal PurchaseAdvBalance { get; set; } + public System.DateTime SkipDate { get; set; } + public bool ToBeDelinquent { get; set; } + public bool WaiveDelinquencyNotice { get; set; } + public bool WaiveBillingNotice { get; set; } + public bool WaiveInterest { get; set; } + public decimal LastStatementMinimumPayment { get; set; } + + public decimal LastStatementCycleRequiredPayment { get; set; } + + public bool ExemptNonResident { get; set; } + public string YTDNonresidentWithholding { get; set; } + public string PYTDNonresidentWithholding { get; set; } + + public string InCollection { get; set; } + + public string UniversalLoanId { get; set; } + public System.DateTime RateLockDate { get; set; } + public string ActiveStopPays { get; set; } + public string[] ErrorMessage { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanOwner.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanOwner.cs new file mode 100644 index 0000000..251fdcb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/LoanOwner.cs @@ -0,0 +1,18 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class LoanOwner + { + public int IndividualId { get; set; } + public string Name { get; set; } + public string Type { get; set; } + public string Address { get; set; } + public string TaxId { get; set; } + public string Association { get; set; } + public System.DateTime LastAssociationCodeChangedDate { get; set; } + public string Metro2ConsumerInformationIndicator { get; set; } + public bool CoborrowerIsSpouse { get; set; } + public decimal OwnershipPercentage { get; set; } + public string PercentageDescription { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/MemberDetail.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/MemberDetail.cs new file mode 100644 index 0000000..caaec37 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/MemberDetail.cs @@ -0,0 +1,55 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class MemberDetail + { + public long MemberId { get; set; } + public string MemberName { get; set; } + public int PrimaryIndividualId { get; set; } + public string MemberType { get; set; } + public string Affiliation { get; set; } + public System.DateTime MemberSince { get; set; } + public string Miscellaneous1 { get; set; } + public string Miscellaneous2 { get; set; } + public string OriginalOpenBranch { get; set; } + public string WithholdingCode { get; set; } + public decimal ExpeditedFunds { get; set; } + public System.DateTime ExpeditedFundsDate { get; set; } + public int AccessLevel { get; set; } + public int StandardStatementCycle { get; set; } + public string StandardStatementCode { get; set; } + public string StandardStatementMethod { get; set; } + public int CATFeeCounter { get; set; } + public System.DateTime CATFeeDate { get; set; } + public int CreditCardStatementCycle { get; set; } + public string CreditCardStatementCode { get; set; } + public string CreditCardStatementMethod { get; set; } + public int AccountCount { get; set; } + public decimal AccountBalance { get; set; } + public int LoanCount { get; set; } + public decimal LoanBalance { get; set; } + public int LoansDelinquentCount { get; set; } + public decimal LoansDelinquent { get; set; } + public int LoansWrittenOffCount { get; set; } + public decimal LoansWrittenOff { get; set; } + public int LoansInCollectionCount { get; set; } + public decimal LoansInCollection { get; set; } + // public AccountSummary[] Account { get; set; } + // public LoanSummary[] Loan { get; set; } + // public CrossMemberDetails[] CrossMember { get; set; } + public string Password { get; set; } + public string PinLock { get; set; } + public int BranchId { get; set; } + public int RelationshipPricingLevel { get; set; } + public string RelationshipPricingLevelPermanent { get; set; } + public string RelationshipPricingDescription { get; set; } + public int PreviousRelationshipPricingLevel { get; set; } + public string LastRelationshipPricingLevelAnalysisDate { get; set; } + public string LastRelationshipPricingLevelChangeDate { get; set; } + public string InhibitActivity { get; set; } + public string WrittenOffLoan { get; set; } + public string LoanInCollection { get; set; } + public string PurgeMembership { get; set; } + public AddressDetail Address { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/OtherCollateral.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/OtherCollateral.cs new file mode 100644 index 0000000..f5e1c03 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/OtherCollateral.cs @@ -0,0 +1,10 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class OtherCollateral + { + public string MiscellaneousInformation { get; set; } + public AppraisalDetail Appraisal { get; set; } + public string OtherCollateralType { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/QueueResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/QueueResult.cs new file mode 100644 index 0000000..6d8bf2e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/QueueResult.cs @@ -0,0 +1,11 @@ + +using LCU.API.Internal.RepoApi.Repos.Models.Producer; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class QueueResult + { + public ulong DeliveryTag { get; set; } + public ProcessingReceipt Receipt { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/RepoRunInfo.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/RepoRunInfo.cs new file mode 100644 index 0000000..6117430 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/RepoRunInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class RepoRunInfo + { + public RepoRunInfo() + { + StartDateTime = DateTime.Now; + Receipts = new List(); + } + + public DateTime StartDateTime { get; private set; } + public DateTime? CompleteDateTime { get; set; } + + public List Receipts { get; private set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/Vehicle.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/Vehicle.cs new file mode 100644 index 0000000..671af9f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Consumer/Vehicle.cs @@ -0,0 +1,20 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Consumer +{ + public class Vehicle + { + public string VIN { get; set; } + public int Year { get; set; } + public string Make { get; set; } + public string Model { get; set; } + public string LicensePlate { get; set; } + public string LicenseState { get; set; } + public string CountryLicensed { get; set; } + public int Mileage { get; set; } + public string BodyStyle { get; set; } + public string Color { get; set; } + public string KeyCode { get; set; } + public string SellerName { get; set; } + public decimal LowBookValue { get; set; } + public decimal HighBookValue { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EnvironmentView.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EnvironmentView.cs new file mode 100644 index 0000000..ba205cc --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EnvironmentView.cs @@ -0,0 +1,33 @@ +using System; +using System.Reflection; +using Microsoft.AspNetCore.Hosting; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class EnvironmentView : IEnvironmentView + { + public string Application => _env.ApplicationName; + public DateTime CurrentDate => DateTime.Now; + public string BuildVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString(); + public DateTime BuildDate => AssemblyBuildDate(); + public string Environment => _env.EnvironmentName; + private readonly IHostingEnvironment _env; + + public EnvironmentView(IHostingEnvironment env) + { + _env = env; + } + + public static DateTime AssemblyBuildDate() + { + //https://stackoverflow.com/a/804310/266594 + //TODO #246: Look up this value at start up to avoid hitting the filesystem repeatedly + System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); + System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location); + DateTime lastModified = fileInfo.LastWriteTime; + + return lastModified; + } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EventAlert.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EventAlert.cs new file mode 100644 index 0000000..ac09ecf --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/EventAlert.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class EventAlert + { + public int Id { get; set; } + public int? RepoId { get; set; } + [Required] + public string AccountNo { get; set; } + [Required] + public DateTime DateAdded { get; set; } + public DateTime? DateMarkedRead { get; set; } + [Required] + [MaxLength(25)] + public string CreatedBy { get; set; } + public string MarkedReadBy { get; set; } + [Required] + public string Message { get; set; } + public int? DayCount { get; set; } + public string SubMessage { get; set; } + public string EmailRecipient { get; set; } + public string EmailBody { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ExternalUser.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ExternalUser.cs new file mode 100644 index 0000000..cc49724 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ExternalUser.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class ExternalUser + { + [Required] + public int Id { get; set; } + + [Required] + public int ReferenceId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public string Code { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationParams.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationParams.cs new file mode 100644 index 0000000..2649e5f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationParams.cs @@ -0,0 +1,13 @@ + +using System.Collections.Generic; +using LCU.API.Internal.RepoApi.Repos.WebServices.NADA; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class FloorPriceCalculationParams + { + public ImsAutoFileData ImsAutoFileData { get; set; } + public CollateralValuation CollateralValuation { get; set; } + public IEnumerable VehicleFloorPricingParameters { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResult.cs new file mode 100644 index 0000000..9f2abf9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResult.cs @@ -0,0 +1,13 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class FloorPriceCalculationResult + { + public string StrategyName { get; set; } + public bool Success { get; set; } + public decimal FloorPrice { get; set; } + public int ResultWeight { get; set; } + public string FailureReason { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResultSet.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResultSet.cs new file mode 100644 index 0000000..ffbc854 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceCalculationResultSet.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class FloorPriceCalculationResultSet + { + public FloorPriceCalculationResult PrimaryResult { get; set; } + public IEnumerable AllResults { get; set; } + public FloorPriceValuesUsed FloorPriceValuesUsed { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceValuesUsed.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceValuesUsed.cs new file mode 100644 index 0000000..fe4f58e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/FloorPriceValuesUsed.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using LCU.API.Internal.RepoApi.Repos.WebServices.NADA; +using Newtonsoft.Json; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class FloorPriceValuesUsed + { + [Required] + public int AuctionAssignmentId { get; set; } + [Required] + public int RepoId { get; set; } + [Required] + public int CollateralValuationId { get; set; } + + public decimal? Condition { get; set; } + public decimal LowParam { get; set; } + public decimal HighParam { get; set; } + public decimal PercentageOf { get; set; } + public decimal? AuctionGuideBookPrice { get; set; } + public bool WasAuctionGuideBookPriceUsed { get; set; } + public decimal? MMRNationalAdjustedPrice { get; set; } + public decimal? MMRRegionalAdjustedPrice { get; set; } + public bool WasMMRNationalAdjustedPriceUsed { get; set; } + public bool WasMMRRegionalAdjustedPriceUsed { get; set; } + public int NADALowAdjustedAuctionValue { get; set; } + public int NADAAverageAdjustedAuctionValue { get; set; } + public int NADAHighAdjustedAuctionValue { get; set; } + public bool WasNADALowAdjustedAuctionValueUsed { get; set; } + public bool WasNADAAverageAdjustedAuctionValueUsed { get; set; } + public bool WasNADAHighAdjustedAuctionValueUsed { get; set; } + public decimal? NADAMMRFloorPrice { get; set; } + + public CollateralValuation CollateralValuation { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/IEnvironmentView.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/IEnvironmentView.cs new file mode 100644 index 0000000..addffee --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/IEnvironmentView.cs @@ -0,0 +1,13 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public interface IEnvironmentView + { + string Application { get; } + DateTime CurrentDate { get; } + string BuildVersion { get; } + DateTime BuildDate { get; } + string Environment { get; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ImsAutoFileData.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ImsAutoFileData.cs new file mode 100644 index 0000000..5315130 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ImsAutoFileData.cs @@ -0,0 +1,77 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class ImsAutoFileData + { + [Required] + public int RepoId { get; set; } + [Required] + public string VIN { get; set; } + [Required] + public int AuctionAssignmentId { get; set; } + [Required] + public string AuctionCustomerNo { get; set; } + + public DateTime? AssignmentAccepted { get; set; } + public DateTime? DateReceivedFromAutoIMS { get; set; } + public DateTime? ScheduledSaleDate { get; set; } + public DateTime? SoldDate { get; set; } + public string AnnouncedConditions { get; set; } + public string ArbitrationReason { get; set; } + public bool? AsIs100KMiles { get; set; } + public int? AuctionAge { get; set; } + public decimal? AuctionGuideBookPrice { get; set; } + public string BuyerId { get; set; } + public string BuyerName { get; set; } + public decimal? ChargeAmount { get; set; } + public decimal? CheckAmount { get; set; } + public int? ClientAge { get; set; } + public DateTime? ClientCancelled { get; set; } + public string CollisionDamage { get; set; } + public DateTime? InspectedDate { get; set; } + public DateTime? DateofLoss { get; set; } + public DateTime? DatePickedUp { get; set; } + public DateTime? DateEnteredAutoIMS { get; set; } + public int? DaysRun { get; set; } + public string DaysRunHighestBidRecordedbyAuction { get; set; } + public int? DaystoDelivery { get; set; } + public string Description { get; set; } + public bool? IsDrivable { get; set; } + public decimal? FloorPriceAccepted { get; set; } + public DateTime? FundsSent { get; set; } + public decimal? GrossPercentFloor { get; set; } + public decimal? HighestBid { get; set; } + public int? StatedMileage { get; set; } + public decimal? NetPercentFloor { get; set; } + public decimal? OtherAdvanceFees { get; set; } + public string OTHERTotal { get; set; } + public string OTHERAdvancedOther { get; set; } + public string OTHERAdvancedStorage { get; set; } + public string OTHERAdvancedTow { get; set; } + public string OTHERMiscellaneous { get; set; } + public decimal? SalePrice { get; set; } + public string TitleNumber { get; set; } + public DateTime? TitleReceived { get; set; } + public DateTime? TitleReturned { get; set; } + public string TitleState { get; set; } + public string TitleStatus { get; set; } + public string TitleStatusfromAuction { get; set; } + public decimal? TotalCharges { get; set; } + public decimal? ApprovedVehicleGrade { get; set; } + public bool? IsOperable { get; set; } + public DateTime? ShipmentDate { get; set; } + public string Status { get; set; } + public decimal? StorageCost { get; set; } + public decimal? StorageRatePerDay { get; set; } + public decimal? TowCost { get; set; } + public decimal? VehicleDecimalGradeCode { get; set; } + public decimal? VehicleGrade { get; set; } + public decimal? MMRNationalAdjustedPrice { get; set; } + public decimal? MMRRegionalAdjustedPrice { get; set; } + public string MMR { get; set; } + public string ConditionReportLink { get; set; } + public string CollateralImagePath { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Letter.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Letter.cs new file mode 100644 index 0000000..90f5f95 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Letter.cs @@ -0,0 +1,20 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class Letter + { + public string ActionCode { get; set; } + + // This is the eventual "This works and the code was written well" solution. + // Once letter codes are in place and agreed upon the alerts can be switched from ActionCode to LetterCode + public string? LetterCode { get; set; } + public string Description { get; set; } + public int? DaysToWait { get; set; } + public string WhenLetterIsSent { get; set; } + + // This is currently used to match up descriptions from Temenos history/comments. Used to find if a specific + // ActionCode/LetterCode was sent. + public string LetterDescription { get; set; } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/OwnershipType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/OwnershipType.cs new file mode 100644 index 0000000..4a997d8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/OwnershipType.cs @@ -0,0 +1,9 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public enum OwnershipType + { + Other = 1, + Coborrower = 3, + Borrower = 6 + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ProcessingReceipt.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ProcessingReceipt.cs new file mode 100644 index 0000000..e3865dd --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/ProcessingReceipt.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class ProcessingReceipt + { + public ProcessingReceipt(string accountNo) + { + AccountNo = accountNo; + AccountId = ""; + QueuedBy = "Unknown"; + Message = ""; + Outcome = RepoProcessingResult.Error; + Processed = DateTime.Now; + } + + public string AccountNo { get; protected set; } + + [Required] + public string AccountId { get; set; } + [Required] + public string CaseId { get; set; } + [Required] + [MaxLength(40)] + public string QueuedBy { get; set; } + public RepoProcessingResult Outcome { get; set; } + public string Message { get; set; } + public DateTime Processed { get; set; } + + public string Pretty() + { + return $"Key: {AccountNo}|{AccountId}|{CaseId}|{QueuedBy} -> {Outcome} : {Message}"; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/ProcessingItem.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/ProcessingItem.cs new file mode 100644 index 0000000..87c6c88 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/ProcessingItem.cs @@ -0,0 +1,18 @@ + +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Producer +{ + public class ProcessingItem + { + public string AccountNo { get; set; } + + public string AccountId { get; set; } + public string CaseId { get; set; } + public string QueuedBy { get; set; } + public string DetermineAuditItemKey() + { + return AccountNo + ":" + CaseId; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/RepoProcessingResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/RepoProcessingResult.cs new file mode 100644 index 0000000..dce5cff --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Producer/RepoProcessingResult.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Producer +{ + public enum RepoProcessingResult + { + Autoclosed, + Unconverted, + Created, + Updated, + Ineligible, + MissingRequirement, + Error + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RefundStatus.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RefundStatus.cs new file mode 100644 index 0000000..ea992e0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RefundStatus.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RefundStatus + { + [Required] + public int? RepoId { get; set; } + public string? WarrantyRefundStatus { get; set; } + public string? InsuranceRefundStatus { get; set; } + public string? PremiumDefiRefundStatus { get; set; } + public string? CPIRefundStatus { get; set; } + public DateTime? RefundsReceivedDate { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoAccount.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoAccount.cs new file mode 100644 index 0000000..94f4653 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoAccount.cs @@ -0,0 +1,58 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoAccount + { + public int RepoId { get; set; } + [Required] + public string AccountNo { get; set; } + [Required] + public string Status { get; set; } + public long MemberNo { get; set; } + public long LoanNo { get; set; } + public string AccountTypeCode { get; set; } + public string OrigAccountTypeCode { get; set; } + public int? Branch { get; set; } + public string Collector { get; set; } + public decimal? Balance { get; set; } + public DateTime? DueToPayDate { get; set; } + public DateTime? O4RDate { get; set; } + public DateTime? DateFirstDelinquent { get; set; } + public decimal? DelinquentAmount { get; set; } + public int? DaysDelinquent { get; set; } + public decimal? PaidSkipAmount { get; set; } + public string SubStatus { get; set; } + public decimal? RequiredPaymentAmount { get; set; } + public decimal? CurrentDelinquentAmount { get; set; } + public decimal? LstPmtAmt { get; set; } + public decimal? OrigAmount { get; set; } + public DateTime? LastPaymentDate { get; set; } + public DateTime? DateFirstIntellect { get; set; } + public string PrimaryIndividualId { get; set; } + public string Bankruptcy { get; set; } + public DateTime? ManualRedemptionDate { get; set; } + public DateTime? RedemptionExpiredDate { get; set; } + public ICollection Borrowers { get; set; } + public ICollection Collaterals { get; set; } + public ICollection RepoStatusChanges { get; set; } + [ForeignKey("RepoId")] + public RefundStatus RefundStatus { get; set; } + public DateTime? DateClosed { get; set; } + // This should reference + // a CaseId for Temenos. + public int? ReferenceId { get; set; } + public int? ReferenceCaseId { get; set; } + // This is the "Account Status" from Temenos so we can "Watch" it. + public string ReferenceStatus { get; set; } + public DateTime? AccountLastProcessed { get; set; } + public DateTime? WrittenOffDate { get; set; } + public int? SixtyPlusCollectorId { get; set; } + [ForeignKey("SixtyPlusCollectorId")] + public SixtyPlusCollector SixtyPlusCollector { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompany.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompany.cs new file mode 100644 index 0000000..7e304c2 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompany.cs @@ -0,0 +1,22 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoCompany + { + public int ID { get; set; } + + public int? ReferenceId { get; set; } + public string Name { get; set; } + public string? Email { get; set; } + public string? Address_Line_1 { get; set; } + public string? Address_Line_2 { get; set; } + public string? City { get; set; } + public string? State { get; set; } + public string? ZIP { get; set; } + public string? Country { get; set; } + public string? Phone { get; set; } + public string? Fax { get; set; } + public string? DisabledBy { get; set; } + public DateTime? DisabledDate { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompanySelection.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompanySelection.cs new file mode 100644 index 0000000..32c192d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoCompanySelection.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using LCU.API.Collections.CollectionsApiLib.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoCompanySelection : IValidatableObject + { + public int? Id { get; set; } + + [Required] + public int? CollateralId { get; set; } + + public RepoType RepoType { get; set; } + + public int? RepoCompanyId { get; set; } + + public DateTime? RepoCompanyAssignmentDate { get; set; } + + public DateTime? DateRepoed { get; set; } + + public DateTime? DateTimeCreated { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + var failedValidations = new List(); + + // This cannot be a required field on account of it resulting in a SQL error + if (RepoType == null) + { + failedValidations.Add(new ValidationResult("Repo Company Selection: The RepoType cannot be blank")); + } + + if (RepoCompanyAssignmentDate > DateRepoed) + { + failedValidations.Add(new ValidationResult("Repo Company Selection: The date repoed cannot come before the date assigned.")); + } + + if (RepoCompanyId != null && RepoCompanyAssignmentDate == null) + { + failedValidations.Add(new ValidationResult("Repo Company Selection: The Repo Company Assignment date cannot be null when a Repo Company is set.")); + } + + return failedValidations; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoLetter.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoLetter.cs new file mode 100644 index 0000000..c0894d9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoLetter.cs @@ -0,0 +1,14 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoLetter + { + public string NoticeType { get; set; } + public DateTime DateSent { get; set; } + public DateTime DateExpired { get; set; } + public int DaysToRedeem { get; set; } + public string ActionCode { get; set; } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoManualStatus.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoManualStatus.cs new file mode 100644 index 0000000..a20d045 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoManualStatus.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoManualStatus + { + [Required] + public int ID { get; set; } + [Required] + public string Description { get; set; } + [Required] + public bool IsActiveStatus { get; set; } + [Required] + public bool WatchForStatusChanges { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoStatusChange.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoStatusChange.cs new file mode 100644 index 0000000..eaec16f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/RepoStatusChange.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class RepoStatusChange + { + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + [Required] + public int RepoId { get; set; } + [Required] + public string StatusFrom { get; set; } + [Required] + public string StatusTo { get; set; } + [Required] + public string ChangedBy { get; set; } + [Required] + public DateTime ChangedDate { get; set; } + + [ForeignKey("RepoId")] + [JsonIgnore] + public RepoAccount RepoAccount { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/LandmarkReportAccountEntry.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/LandmarkReportAccountEntry.cs new file mode 100644 index 0000000..f10a4eb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/LandmarkReportAccountEntry.cs @@ -0,0 +1,47 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting +{ + public class LandmarkReportAccountEntry + { + // Repo + public int RepoId { get; set; } + public string AccountNo { get; set; } + public decimal? Balance { get; set; } + public int? DaysDelinquent { get; set; } + public DateTime? LastPaymentDate { get; set; } + public decimal? LastPaymentAmount { get; set; } + public string AccountTypeCode { get; set; } + public string Bankruptcy { get; set; } + public decimal? DelinquentAmount { get; set; } + public string Collector { get; set; } + public DateTime? DateClosed { get; set; } + // Borrower + public string IndividualName { get; set; } + // Collateral + public string Make { get; set; } + public string Model { get; set; } + public int? Year { get; set; } + // RepoCompanySelection + public string RepoCompany { get; set; } + public DateTime? DateRepoed { get; set; } + public string RepoType { get; set; } + public DateTime? DateAssigned { get; set; } + // RepoStatusChange + public string Status { get; set; } + public int? DaysInStatus { get; set; } + // IMSAutoFile Data + public DateTime? SoldDate { get; set; } + public DateTime? DateEnteredAutoIMS { get; set; } + public DateTime? ScheduledSaleDate { get; set; } + public decimal? FloorPrice { get; set; } + public decimal? Grade { get; set; } + // Auction and AuctionAssignments + public string AuctionHouse { get; set; } + public DateTime? AuctionDate { get; set; } + public decimal? TotalCharges { get; set; } + public decimal? GrossPercentFloor { get; set; } + public decimal? NetPercentFloor { get; set; } + public decimal? GrossSalePrice { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/DataTable.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/DataTable.cs new file mode 100644 index 0000000..26ae1b7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/DataTable.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting.Responses +{ + public class DataTable + { + public string[] Headers { get; set; } + public IEnumerable Rows { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/RepoAccountReferenceRow.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/RepoAccountReferenceRow.cs new file mode 100644 index 0000000..cc7b7c8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/RepoAccountReferenceRow.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting.Responses +{ + public class RepoAccountReferenceRow + { + public int RepoId { get; set; } + public ReportCell[] Cells { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportCell.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportCell.cs new file mode 100644 index 0000000..fd7176b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportCell.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting.Responses +{ + public class ReportCell + { + public string Display { get; set; } + public object Value { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTable.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTable.cs new file mode 100644 index 0000000..cc2867e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTable.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting.Responses +{ + public class ReportTable + { + public string[] Headers { get; set; } + public ReportCell[,] Cells { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTableResponse.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTableResponse.cs new file mode 100644 index 0000000..52d8264 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/Responses/ReportTableResponse.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting.Responses +{ + public class ReportTableResponse + { + public ReportTable ReportTable { get; set; } + public DataTable DataTable { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/StdReportEntry.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/StdReportEntry.cs new file mode 100644 index 0000000..899cb61 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Reporting/StdReportEntry.cs @@ -0,0 +1,53 @@ +using System; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Reporting +{ + public class StdReportEntry + { + // Repo + public int RepoId { get; set; } + public string AccountNo { get; set; } + public string AccountTypeCode { get; set; } + public decimal Balance { get; set; } + public string Status { get; set; } + public int DaysInStatus { get; set; } + public string Bankruptcy { get; set; } + public string Collector { get; set; } + public int? DaysCurrentDelq { get; set; } + public int? DaysDelqWhenAdded { get; set; } + public decimal? DelinquentAmount { get; set; } + public decimal? LastPaymentAmount { get; set; } + public DateTime? LastPaymentDate { get; set; } + public string FilteredStatus { get; set; } + public DateTime? FilteredStatusChangeDate { get; set; } + public DateTime? O4RDate { get; set; } + // Auction Assignment + public string AuctionId { get; set; } + public string AuctionName { get; set; } + // Collateral + public int CollateralId { get; set; } + public int? RepoCompanyId { get; set; } + public string RepoCompany { get; set; } + public string RepoType { get; set; } + public DateTime? RepoCompanyAssignmentDate { get; set; } + public DateTime? DateRepoed { get; set; } + public int? RepoDuration { get; set; } + public int? Year { get; set; } + public string Make { get; set; } + public string Model { get; set; } + // IMSAutoFile Data + public string AutoImsStatus { get; set; } + public DateTime? SoldDate { get; set; } + public DateTime? DateEnteredAutoIMS { get; set; } + public DateTime? ScheduledSaleDate { get; set; } + public decimal? GrossPercentFloor { get; set; } + public decimal? NetPercentFloor { get; set; } + public decimal? TotalCharges { get; set; } + public decimal? Grade { get; set; } + public decimal? FloorPrice { get; set; } + public decimal? GrossSalePrice { get; set; } + // Borrower + public string IndividualName { get; set; } + } + +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionData.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionData.cs new file mode 100644 index 0000000..64632fb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionData.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Results +{ + public class AuctionData + { + public int LowAuction { get; set; } + public int AvgAuction { get; set; } + public int HighAuction { get; set; } + public string DateRange { get; set; } + public object AuctionAccessories { get; set; } + public string AcceptableMileage { get; set; } + public string RatePerMile { get; set; } + public string MileageAdjust { get; set; } + public string MinAuctionValue { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionInformation.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionInformation.cs new file mode 100644 index 0000000..b1c5ae1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/AuctionInformation.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Results +{ + public class AuctionInformation + { + public string RequestId { get; set; } + public List Result { get; set; } + public string Userinfo { get; set; } + public string Error { get; set; } + public string AuthId { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/PagedResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/PagedResult.cs new file mode 100644 index 0000000..71cd7d3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/PagedResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Results +{ + public class PagedResult where T : class + { + public IEnumerable Items { get; set; } + public int Total { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleData.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleData.cs new file mode 100644 index 0000000..1749963 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleData.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Results +{ + public class VehicleData + { + public int Modelyear { get; set; } + public string Make { get; set; } + public string Model { get; set; } + public string Body { get; set; } + public int Ucgvehicleid { get; set; } + public string Vehicletype { get; set; } + public string Mileageclass { get; set; } + public string Basemsrp { get; set; } + public string Bodytype { get; set; } + public string Doors { get; set; } + public string Trim { get; set; } + public string Trim2 { get; set; } + public string Drivetype { get; set; } + public string Liters { get; set; } + public string Engineconfiguration { get; set; } + public string Cylinders { get; set; } + public string Inductiontype { get; set; } + public string Transmission { get; set; } + public string Fueltype { get; set; } + public string Wheels { get; set; } + public string Curbweight { get; set; } + public string Gvw { get; set; } + public string Gcw { get; set; } + public string Ucgsubsegment { get; set; } + public string Modelnumber { get; set; } + public int Rollupvehicleid { get; set; } + public int Basecleantrade { get; set; } + public int Baseaveragetrade { get; set; } + public int Baseroughtrade { get; set; } + public int Basecleanretail { get; set; } + public int Baseloan { get; set; } + public int Averagemileage { get; set; } + public int Mileageadjustment { get; set; } + public int Vinoptionstrade { get; set; } + public int Vinoptionsretail { get; set; } + public int Vinoptionsloan { get; set; } + public int Adjustedcleantrade { get; set; } + public int Adjustedaveragetrade { get; set; } + public int Adjustedroughtrade { get; set; } + public int Adjustedcleanretail { get; set; } + public int Adjustedloan { get; set; } + public int Maxmileageadj { get; set; } + public int Minmileageadj { get; set; } + public int Minadjretail { get; set; } + public int Minadjcleantrade { get; set; } + public int Minadjaveragetrade { get; set; } + public int Minadjroughtrade { get; set; } + public int Minadjloan { get; set; } + public int Minadjretailforloan { get; set; } + public int Vid { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleInformation.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleInformation.cs new file mode 100644 index 0000000..53e5678 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Results/VehicleInformation.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Results +{ + public class VehicleInformation + { + public string RequestId { get; set; } + public List Result { get; set; } + public string Userinfo { get; set; } + private string AdjustedMileage { get; set; } + private string AuthId { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SavedLocation.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SavedLocation.cs new file mode 100644 index 0000000..fc3e91a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SavedLocation.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class SavedLocation + { + public int ID { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public int? LocationType { get; set; } + + [Required] + public string Address_Line_1 { get; set; } + + public string Address_Line_2 { get; set; } + + [Required] + public string City { get; set; } + + [Required] + public string State { get; set; } + + [Required] + public string ZIP { get; set; } + + public string Country { get; set; } + + public string Phone { get; set; } + + public string Fax { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AccountType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AccountType.cs new file mode 100644 index 0000000..84ec45c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AccountType.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class AccountType + { + [Required] + public int ID { get; set; } + [Required] + public string AccountTypeCode { get; set; } + [Required] + public string Description { get; set; } + [Required] + public bool IsVisible { get; set; } + public int? CategoryId { get; set; } + + [ForeignKey("ID")] + [JsonIgnore] + public LoanCategory LoanCategories { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AllowedStatusChange.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AllowedStatusChange.cs new file mode 100644 index 0000000..61ee301 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/AllowedStatusChange.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class AllowedStatusChange + { + [Required] + public int ID { get; set; } + [Required] + public string CurrentStatus { get; set; } + [Required] + public string AllowedChangeStatus { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BodyStylesCodes.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BodyStylesCodes.cs new file mode 100644 index 0000000..e42aa8d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BodyStylesCodes.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class BodyStyle + { + [Required] + public string Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BranchEntry.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BranchEntry.cs new file mode 100644 index 0000000..ec3bbd9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/BranchEntry.cs @@ -0,0 +1,9 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class BranchEntry + { + public string BranchId { get; set; } + public string BranchName { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevel.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevel.cs new file mode 100644 index 0000000..e8e8fb9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevel.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class CleaningLevel + { + [Required] + public int Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevelCodes_NN.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevelCodes_NN.cs new file mode 100644 index 0000000..5aea6ef --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CleaningLevelCodes_NN.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class CleaningLevelCodes_NN + { + [Required] + public int Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CodeDescriptionPairs.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CodeDescriptionPairs.cs new file mode 100644 index 0000000..80b5fce --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CodeDescriptionPairs.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class CodeDescriptionPairs + { + [Required] + public int Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralType.cs new file mode 100644 index 0000000..1d7b912 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralType.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class CollateralType + { + [Required] + public int ID { get; set; } + [Required] + public string Description { get; set; } + [Required] + public string LookupHandler { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralTypeToVehicleType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralTypeToVehicleType.cs new file mode 100644 index 0000000..c475613 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/CollateralTypeToVehicleType.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class CollateralTypeToVehicleType + { + [Required] + public int ID { get; set; } + [Required] + public int CollateralTypeId { get; set; } + [Required] + public int VehicleTypeId { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/HoldStatus.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/HoldStatus.cs new file mode 100644 index 0000000..e4eb49d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/HoldStatus.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class HoldStatus + { + [Required] + public int Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LoanCategory.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LoanCategory.cs new file mode 100644 index 0000000..87d8cf4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LoanCategory.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class LoanCategory + { + [Required] + public int ID { get; set; } + [Required] + public string CategoryName { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LocationType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LocationType.cs new file mode 100644 index 0000000..39cac9d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/LocationType.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class LocationType + { + [Required] + public int Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/Settings.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/Settings.cs new file mode 100644 index 0000000..82f28d0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/Settings.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class Settings + { + public IEnumerable AccountTypes { get; set; } + public IEnumerable AllowedStatusChanges { get; set; } + public IEnumerable BodyStyles { get; set; } + public IEnumerable CleaningLevels { get; set; } + public IEnumerable CollateralTypes { get; set; } + public IEnumerable CollateralTypesToVehicleTypes { get; set; } + public IEnumerable HoldStatuses { get; set; } + public IEnumerable LoanCategories { get; set; } + public IEnumerable LocationTypes { get; set; } + public IEnumerable VehicleTypes { get; set; } + public IEnumerable BranchSet { get; set; } + public IReadOnlyCollection ExternalUsers { get; set; } + public IReadOnlyCollection RepoCompanies { get; set; } + public IReadOnlyCollection TransactionTypeCodes { get; set; } + public string TransactionTypeCodeQueryPrefix { get; set; } + public string TransactionHistoryParameterDateFormat { get; set; } + public int TransactionHistoryStartDateOffset { get; set; } + public IReadOnlyDictionary RepoTypeMap { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/VehicleType.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/VehicleType.cs new file mode 100644 index 0000000..e493192 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/Settings/VehicleType.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models.Settings +{ + public class VehicleType + { + [Required] + public string Code { get; set; } + [Required] + public string Description { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SixtyPlusCollector.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SixtyPlusCollector.cs new file mode 100644 index 0000000..90fbc1a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/SixtyPlusCollector.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class SixtyPlusCollector + { + [Required] + public int SixtyPlusCollectorId { get; set; } + [Required] + public string SixtyPlusCollectorName { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/VehicleFloorPricingParameters.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/VehicleFloorPricingParameters.cs new file mode 100644 index 0000000..353cab5 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Models/VehicleFloorPricingParameters.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.Models +{ + public class VehicleFloorPricingParameters + { + [Required] + public int Id { get; set; } + public decimal LowCondition { get; set; } + public decimal HighCondition { get; set; } + public string NADAFirstValue { get; set; } + public string NADASecondValue { get; set; } + public string MMRAdjustedValue { get; set; } + public decimal PercentageOf { get; set; } + public int ValuationServiceType { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/AppDbContext.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/AppDbContext.cs new file mode 100644 index 0000000..e97cacb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/AppDbContext.cs @@ -0,0 +1,412 @@ +using Microsoft.EntityFrameworkCore; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Reporting; +using System.Data.Entity.Infrastructure; +using LCU.Library.Internal.RepoApiModels.Models; + + +namespace LCU.API.Internal.RepoApi.Repos.Persistence +{ + public class AppDbContext : DbContext + { + public DbSet AccountTypeCodes { get; set; } + public DbSet AllowedStatusChanges { get; set; } + public DbSet Auctions { get; set; } + public DbSet AuctionAssignments { get; set; } + public DbSet AuctionAssignmentNotes { get; set; } + public DbSet AuditRecords { get; set; } + public DbSet BodyStyleCodes { get; set; } + public DbSet Borrowers { get; set; } + public DbSet CleaningLevelCodes { get; set; } + public DbSet Collaterals { get; set; } + public DbSet CollateralTypes { get; set; } + public DbSet CollateralTypesToVehicleTypes { get; set; } + public DbSet EventAlerts { get; set; } + public DbSet FloorPriceValuesUsed { get; set; } + public DbSet HoldStatusCodes { get; set; } + public DbSet ImsAutoFileData { get; set; } + public DbSet Letters { get; set; } + public DbSet LoanCategories { get; set; } + public DbSet LocationTypeCodes { get; set; } + public DbSet ProcessingReceipts { get; set; } + public DbSet RepoAccounts { get; set; } + public DbSet RepoCompanies { get; set; } + public DbSet RepoManualStatuses { get; set; } + public DbSet RepoStatusChanges { get; set; } + public DbSet VehicleTypeCodes { get; set; } + public DbSet VehicleFloorPricingParameters { get; set; } + public DbSet RefundStatuses { get; set; } + public DbSet AuctionAssignmentToCollaterals { get; set; } + public DbSet RepoCompanySelections { get; set; } + public DbSet SavedLocations { get; set; } + + public DbSet ExternalUsers { get; set; } + public DbSet SixtyPLusCollectors { get; set; } + + public DbSet RepoSyncRunInfo { get; set; } + public DbSet RepoSyncBatch { get; set; } + + // Queries for Reporting / etc + //TODO REMOVING FOR testing + //public DbQuery StandardReportEntries { get; set; } + //public DbQuery ActiveAccountEntries { get; set; } + + public AppDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity().ToTable("EventAlert"); + builder.Entity().HasKey(p => p.Id); + builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.AccountNo).IsRequired(); + builder.Entity().Property(p => p.DateAdded).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.CreatedBy).IsRequired().HasMaxLength(25); + builder.Entity().Property(p => p.Message).IsRequired(); + builder.Entity().Property(p => p.EmailRecipient); + builder.Entity().Property(p => p.EmailBody); + + builder.Entity().ToTable("Borrowers"); + builder.Entity().HasKey(p => new { p.RepoId, p.IndividualId }); + builder.Entity().Property(p => p.IndividualName).IsRequired(); + builder.Entity().Property(p => p.OwnershipType).IsRequired(); + + builder.Entity().ToTable("ProcessingReceipt"); + builder.Entity().HasKey(p => new { p.AccountNo, p.AccountId, p.CaseId }); + builder.Entity().Property(p => p.AccountId); + builder.Entity().Property(p => p.CaseId); + builder.Entity().Property(p => p.QueuedBy); + builder.Entity().Property(p => p.Message); + //TODO: DO this for repocompanyselection repo type + builder.Entity().Property(p => p.Outcome) + .HasConversion(x => x.ToString(), + x => (RepoProcessingResult)Enum.Parse(typeof(RepoProcessingResult), x)); + builder.Entity().Property(p => p.Processed); + + builder.Entity().ToTable("Repos"); + builder.Entity().HasKey(p => p.RepoId); + builder.Entity().Property(p => p.RepoId).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.AccountNo).IsRequired(); + builder.Entity().Property(p => p.Status).IsRequired(); + builder.Entity().Property(p => p.SubStatus); + builder.Entity().HasMany(p => p.Borrowers); + builder.Entity().HasMany(p => p.Collaterals); + builder.Entity().HasMany(p => p.RepoStatusChanges); + builder.Entity().HasOne(p => p.RefundStatus).WithOne().IsRequired(false); + builder.Entity().Property(p => p.RedemptionExpiredDate); + builder.Entity().HasOne(p => p.SixtyPlusCollector); + + builder.Entity().ToTable("RepoStatusChanges"); + builder.Entity().HasKey(p => new { p.RepoId, p.Id }); + builder.Entity().Property(p => p.RepoId).IsRequired(); + builder.Entity().Property(p => p.StatusFrom).IsRequired(); + builder.Entity().Property(p => p.StatusTo).IsRequired(); + builder.Entity().Property(p => p.ChangedBy).IsRequired(); + builder.Entity().Property(p => p.ChangedDate).IsRequired().ValueGeneratedOnAdd(); + + builder.Entity().ToTable("AuditTable"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.DatePerformed).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.Description).IsRequired(); + builder.Entity().Property(p => p.Action).IsRequired(); + builder.Entity().Property(p => p.ClassName).IsRequired(); + builder.Entity().Property(p => p.PerformedBy).IsRequired(); + builder.Entity().Property(p => p.KeyRepresentation).IsRequired(); + builder.Entity().Property(p => p.PropertyValues); + + builder.Entity().ToTable("Collaterals"); + builder.Entity().HasKey(p => p.CollateralId); + builder.Entity().Property(p => p.CollateralId).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.RepoId).IsRequired(); + builder.Entity().Property(p => p.Vin); + builder.Entity().Property(p => p.Make); + builder.Entity().Property(p => p.Model); + builder.Entity().Property(p => p.Year); + builder.Entity().Property(p => p.Description); + builder.Entity().Property(p => p.VehicleType); + builder.Entity().Property(p => p.BodyStyle); + builder.Entity().Property(p => p.Inoperable); + builder.Entity().Property(p => p.CollateralTypeId); + builder.Entity().Property(p => p.RetitledInLandmarkName); + builder.Entity().Property(p => p.RefCollateralId); + builder.Entity().HasMany(p => p.AuctionAssignmentToCollaterals); + builder.Entity().HasMany(p => p.RepoCompanySelections); + + builder.Entity().ToTable("RepoManualStatus"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.Description).IsRequired(); + builder.Entity().Property(p => p.IsActiveStatus).IsRequired(); + builder.Entity().Property(p => p.WatchForStatusChanges).IsRequired(); + + builder.Entity().ToTable("AccountTypeCodes"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.Description).IsRequired(); + builder.Entity().Property(p => p.IsVisible).IsRequired(); + builder.Entity().Property(p => p.CategoryId); + + builder.Entity().ToTable("AllowedStatusChanges"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.CurrentStatus).IsRequired(); + builder.Entity().Property(p => p.AllowedChangeStatus).IsRequired(); + + builder.Entity().ToTable("BodyStyleCodes"); + builder.Entity().HasKey(p => p.Code); + builder.Entity().Property(p => p.Code).IsRequired(); + builder.Entity().Property(p => p.Description).IsRequired(); + + builder.Entity().ToTable("CleaningLevelCodes"); + builder.Entity().HasKey(p => p.Code); + builder.Entity().Property(p => p.Code).IsRequired(); + builder.Entity().Property(p => p.Description).IsRequired(); + + builder.Entity().ToTable("CollateralTypes"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.Description).IsRequired(); + builder.Entity().Property(p => p.LookupHandler).IsRequired(); + + builder.Entity().ToTable("HoldStatusCodes"); + builder.Entity().HasKey(p => p.Code); + builder.Entity().Property(p => p.Code).IsRequired(); + builder.Entity().Property(p => p.Description).IsRequired(); + + builder.Entity().ToTable("LoanCategories"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.CategoryName).IsRequired(); + + builder.Entity().ToTable("LocationTypeCodes"); + builder.Entity().HasKey(p => p.Code); + builder.Entity().Property(p => p.Code).IsRequired(); + builder.Entity().Property(p => p.Description).IsRequired(); + + builder.Entity().ToTable("VehicleTypeCodes"); + builder.Entity().HasKey(p => p.Code); + builder.Entity().Property(p => p.Code).IsRequired(); + builder.Entity().Property(p => p.Description).IsRequired(); + + builder.Entity().ToTable("CollateralTypesToVehicleTypes"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.VehicleTypeId).IsRequired(); + + builder.Entity().ToTable("VehicleFloorPricingParameters"); + builder.Entity().HasKey(p => p.Id); + builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.LowCondition).IsRequired(); + builder.Entity().Property(p => p.HighCondition).IsRequired(); + builder.Entity().Property(p => p.NADAFirstValue).IsRequired(); + builder.Entity().Property(p => p.NADASecondValue).IsRequired(); + builder.Entity().Property(p => p.MMRAdjustedValue).IsRequired(); + builder.Entity().Property(p => p.PercentageOf).IsRequired(); + builder.Entity().Property(p => p.ValuationServiceType).IsRequired(); + + builder.Entity().ToTable("Letters"); + builder.Entity().HasKey(p => p.ActionCode); + builder.Entity().Property(p => p.Description).IsRequired(); + builder.Entity().Property(p => p.DaysToWait); + builder.Entity().Property(p => p.WhenLetterIsSent); + + builder.Entity().ToTable("FloorPriceValuesUsed"); + builder.Entity().HasKey(p => new { p.AuctionAssignmentId, p.CollateralValuationId }); + builder.Entity().Property(p => p.AuctionAssignmentId).IsRequired(); + builder.Entity().Property(p => p.RepoId).IsRequired(); + builder.Entity().Property(p => p.CollateralValuationId).IsRequired(); + builder.Entity().Property(p => p.Condition); + builder.Entity().Property(p => p.LowParam); + builder.Entity().Property(p => p.HighParam); + builder.Entity().Property(p => p.PercentageOf); + builder.Entity().Property(p => p.AuctionGuideBookPrice); + builder.Entity().Property(p => p.WasAuctionGuideBookPriceUsed); + builder.Entity().Property(p => p.MMRNationalAdjustedPrice); + builder.Entity().Property(p => p.MMRRegionalAdjustedPrice); + builder.Entity().Property(p => p.WasMMRNationalAdjustedPriceUsed); + builder.Entity().Property(p => p.WasMMRRegionalAdjustedPriceUsed); + builder.Entity().Property(p => p.NADALowAdjustedAuctionValue); + builder.Entity().Property(p => p.NADAAverageAdjustedAuctionValue); + builder.Entity().Property(p => p.NADAHighAdjustedAuctionValue); + builder.Entity().Property(p => p.WasNADALowAdjustedAuctionValueUsed); + builder.Entity().Property(p => p.WasNADAAverageAdjustedAuctionValueUsed); + builder.Entity().Property(p => p.WasNADAHighAdjustedAuctionValueUsed); + builder.Entity().Property(p => p.NADAMMRFloorPrice); + builder.Entity().HasOne(p => p.CollateralValuation); + + // Only added code-referenced fields for below entities + builder.Entity().ToTable("Auctions"); + builder.Entity().HasKey(p => p.AuctionID); + builder.Entity().Property(p => p.AuctionID).IsRequired(); + builder.Entity().Property(p => p.Name); + builder.Entity().Property(p => p.Active); + builder.Entity().Property(p => p.Address_Line_1); + builder.Entity().Property(p => p.Address_Line_2); + builder.Entity().Property(p => p.City); + builder.Entity().Property(p => p.Country); + builder.Entity().Property(p => p.Fax); + builder.Entity().Property(p => p.Latitude); + builder.Entity().Property(p => p.Longitude); + builder.Entity().Property(p => p.Phone); + builder.Entity().Property(p => p.State); + builder.Entity().Property(p => p.ZIP); + + builder.Entity().ToTable("AuctionAssignments"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.AuctionId).IsRequired(); + builder.Entity().Property(p => p.AuctionCustomerNo).IsRequired(); + builder.Entity().Property(p => p.CollateralVIN); + builder.Entity().Property(p => p.DateSubmitted); + builder.Entity().Property(p => p.FloorPrice); + builder.Entity().Property(p => p.FloorPriceResponseCode); + builder.Entity().Property(p => p.FloorPriceResponseDescription); + builder.Entity().Property(p => p.FloorPriceSubmissionErrors); + builder.Entity().Property(p => p.HoldStatus); + builder.Entity().Property(p => p.HoldUntil); + builder.Entity().Property(p => p.RedeemedDate); + builder.Entity().Property(p => p.VehicleType); + builder.Entity().Property(p => p.BodyStyle); + builder.Entity().Property(p => p.Inoperable); + builder.Entity().Property(p => p.RetailValue); + builder.Entity().Property(p => p.AuctionNotes); + builder.Entity().Property(p => p.AvailablePickupDate); + builder.Entity().Property(p => p.LegalSaleDate); + builder.Entity().Property(p => p.CleaningLevel); + builder.Entity().Property(p => p.SubmissionErrors); + builder.Entity().Property(p => p.SubmissionResponseCode); + builder.Entity().Property(p => p.SubmissionResponseDescription); + builder.Entity().Property(p => p.SentTitleDocsDate); + builder.Entity().Property(p => p.TitleSentResponseCode); + builder.Entity().Property(p => p.TitleSentResponseDescription); + builder.Entity().Property(p => p.TitleSentSubmissionErrors); + builder.Entity().Property(p => p.AddedThroughRepoDatabase); + builder.Entity().Property(p => p.IsActive).HasDefaultValue(true); + builder.Entity().HasOne(p => p.AutoIMS); + builder.Entity().HasOne(p => p.Auction); + builder.Entity().HasMany(p => p.AuctionAssignmentToCollaterals); + builder.Entity().HasOne(p => p.FloorPriceValuesUsed); + + builder.Entity().ToTable("AuctionAssignmentNotes"); + builder.Entity().HasKey(p => p.Id); + builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.AuctionAssignmentId).IsRequired(); + builder.Entity().Property(p => p.AuctionCustomerNo).IsRequired(); + builder.Entity().Property(p => p.DateWritten); + builder.Entity().Property(p => p.Origin); + builder.Entity().Property(p => p.Text); + builder.Entity().Property(p => p.Type); + + builder.Entity().ToTable("ImsAutoFileData"); + builder.Entity().HasKey(p => new { p.VIN, p.RepoId }); + builder.Entity().Property(p => p.RepoId).IsRequired(); + builder.Entity().Property(p => p.AuctionCustomerNo).IsRequired(); + builder.Entity().Property(p => p.VIN).IsRequired(); + builder.Entity().Property(p => p.AssignmentAccepted); + builder.Entity().Property(p => p.AuctionAssignmentId); + builder.Entity().Property(p => p.DateReceivedFromAutoIMS); + builder.Entity().Property(p => p.AnnouncedConditions); + builder.Entity().Property(p => p.ApprovedVehicleGrade); + builder.Entity().Property(p => p.ArbitrationReason); + builder.Entity().Property(p => p.AsIs100KMiles); + builder.Entity().Property(p => p.AuctionAge); + builder.Entity().Property(p => p.AuctionGuideBookPrice); + builder.Entity().Property(p => p.BuyerId); + builder.Entity().Property(p => p.BuyerName); + builder.Entity().Property(p => p.ChargeAmount); + builder.Entity().Property(p => p.CheckAmount); + builder.Entity().Property(p => p.ClientAge); + builder.Entity().Property(p => p.ClientCancelled); + builder.Entity().Property(p => p.CollateralImagePath); + builder.Entity().Property(p => p.CollisionDamage); + builder.Entity().Property(p => p.ConditionReportLink); + builder.Entity().Property(p => p.DateEnteredAutoIMS); + builder.Entity().Property(p => p.DatePickedUp); + builder.Entity().Property(p => p.DateofLoss); + builder.Entity().Property(p => p.DaysRun); + builder.Entity().Property(p => p.DaysRunHighestBidRecordedbyAuction); + builder.Entity().Property(p => p.DaystoDelivery); + builder.Entity().Property(p => p.Description); + builder.Entity().Property(p => p.FloorPriceAccepted); + builder.Entity().Property(p => p.FundsSent); + builder.Entity().Property(p => p.GrossPercentFloor); + builder.Entity().Property(p => p.HighestBid); + builder.Entity().Property(p => p.InspectedDate); + builder.Entity().Property(p => p.IsDrivable); + builder.Entity().Property(p => p.IsOperable); + builder.Entity().Property(p => p.MMR); + builder.Entity().Property(p => p.MMRNationalAdjustedPrice); + builder.Entity().Property(p => p.MMRRegionalAdjustedPrice); + builder.Entity().Property(p => p.NetPercentFloor); + builder.Entity().Property(p => p.OTHERAdvancedOther); + builder.Entity().Property(p => p.OTHERAdvancedStorage); + builder.Entity().Property(p => p.OTHERAdvancedTow); + builder.Entity().Property(p => p.OTHERMiscellaneous); + builder.Entity().Property(p => p.OTHERTotal); + builder.Entity().Property(p => p.OtherAdvanceFees); + builder.Entity().Property(p => p.ScheduledSaleDate); + builder.Entity().Property(p => p.SoldDate); + builder.Entity().Property(p => p.Status); + builder.Entity().Property(p => p.SalePrice); + builder.Entity().Property(p => p.ShipmentDate); + builder.Entity().Property(p => p.StorageCost); + builder.Entity().Property(p => p.StorageRatePerDay); + builder.Entity().Property(p => p.StatedMileage); + builder.Entity().Property(p => p.TitleNumber); + builder.Entity().Property(p => p.TitleReceived); + builder.Entity().Property(p => p.TitleReturned); + builder.Entity().Property(p => p.TitleState); + builder.Entity().Property(p => p.TitleStatus); + builder.Entity().Property(p => p.TitleStatusfromAuction); + builder.Entity().Property(p => p.TotalCharges); + builder.Entity().Property(p => p.TowCost); + builder.Entity().Property(p => p.VehicleDecimalGradeCode); + builder.Entity().Property(p => p.VehicleGrade); + + builder.Entity().ToTable("RepoCompany"); + builder.Entity().HasKey(p => p.ID); + builder.Entity().Property(p => p.ID).IsRequired().ValueGeneratedOnAdd(); + builder.Entity().Property(p => p.Name); + builder.Entity().Property(p => p.Email); + + builder.Entity().ToTable("RefundStatuses"); + builder.Entity().HasKey(p => p.RepoId); + + builder.Entity().ToTable("AuctionAssignmentsToCollaterals"); + builder.Entity().HasKey(p => new { p.AuctionAssignmentId, p.CollateralId }); + builder.Entity() + .HasOne(aa2c => aa2c.AuctionAssignment) + .WithMany(auctionAssignment => auctionAssignment.AuctionAssignmentToCollaterals) + .HasForeignKey(aa2c => aa2c.AuctionAssignmentId); + builder.Entity() + .HasOne(aa2c => aa2c.Collateral) + .WithMany(Collateral => Collateral.AuctionAssignmentToCollaterals) + .HasForeignKey(aa2c => aa2c.CollateralId); + + builder.Entity().ToTable("RepoCompanySelections"); + builder.Entity().HasKey(p => p.Id); + builder.Entity().Property(p => p.RepoType) + .HasConversion(x => x.ToString(), + x => (RepoType)Enum.Parse(typeof(RepoType), x)); + builder.Entity().Property(p => p.Processed); + + builder.Entity().ToTable("SavedLocations"); + builder.Entity().HasKey(p => p.ID); + + builder.Entity().ToTable("UserCache"); + builder.Entity().HasKey(p => p.Id); + builder.Entity().Property(p => p.Name); + builder.Entity().Property(p => p.Code); + + builder.Entity().ToTable("SixtyPlusCollector"); + builder.Entity().HasKey(p => p.SixtyPlusCollectorId); + + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/BaseRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/BaseRepository.cs new file mode 100644 index 0000000..100db3a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Persistence/BaseRepository.cs @@ -0,0 +1,13 @@ + +namespace LCU.API.Internal.RepoApi.Repos.Persistence +{ + public abstract class BaseRepository + { + protected readonly AppDbContext _context; + + public BaseRepository(AppDbContext context) + { + _context = context; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentNoteRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentNoteRepository.cs new file mode 100644 index 0000000..f0b1eab --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentNoteRepository.cs @@ -0,0 +1,52 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Persistence; + +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class AuctionAssignmentNoteRepository : BaseRepository, IAuctionAssignmentNoteRepository + { + public AuctionAssignmentNoteRepository(AppDbContext context) : base(context) { } + + public async Task> ListAllAsync(int filterPage, int filterLimit) + { + var results = new PagedResult(); + var skip = (filterPage - 1) * filterLimit; + + var temp = Queryable.OrderByDescending(from x in _context.AuctionAssignmentNotes + select x, x => x.Id); + + results.Total = temp.Count(); + results.Items = await temp.Skip(skip).Take(filterLimit).ToListAsync(); + return results; + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.AuctionAssignmentNotes, c => c.Id == id).FirstOrDefaultAsync(); + } + + public async Task> FindConflictingNotes(DateTime written, string text, string writer) + { + return await Queryable.Where(_context.AuctionAssignmentNotes, c => c.DateWritten == written && c.Text == text && c.Writer == writer).ToListAsync(); + } + + public async Task AddAsync(AuctionAssignmentNote auctionAssignmentNote) + { + var result = await _context.AuctionAssignmentNotes.AddAsync(auctionAssignmentNote); + return result.Entity; + } + + public void Update(AuctionAssignmentNote auctionAssignmentNote) + { + _context.AuctionAssignmentNotes.Update(auctionAssignmentNote); + } + + public void Remove(AuctionAssignmentNote auctionAssignmentNote) + { + _context.AuctionAssignmentNotes.Remove(auctionAssignmentNote); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentRepository.cs new file mode 100644 index 0000000..9bda82f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionAssignmentRepository.cs @@ -0,0 +1,87 @@ +using LCU.API.Internal.RepoApi.Repos.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class AuctionAssignmentRepository : BaseRepository, IAuctionAssignmentRepository + { + public AuctionAssignmentRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync(int filterPage, int filterLimit) + { + var results = new PagedResult(); + var skip = (filterPage - 1) * filterLimit; + + var temp = (from x in _context.AuctionAssignments + select x).OrderByDescending(x => x.ID); + + results.Total = temp.Count(); + results.Items = await temp.Skip(skip).Take(filterLimit).IncludeAll().ToListAsync(); + return results; + } + + public async Task> ListReadyForFloorPriceAsync() + { + var results = new PagedResult(); + + var temp = (from x in _context.AuctionAssignments + where (x.FloorPrice ?? 0) == 0 && x.AutoIMS != null && x.AutoIMS.SoldDate == null && + (x.AutoIMS.StatedMileage ?? -1) > 1 && x.AutoIMS.ScheduledSaleDate != null && + x.AutoIMS.ScheduledSaleDate > DateTime.Now.Date && + x.RedeemedDate == null && + ( (x.AutoIMS.ApprovedVehicleGrade ?? 0) > 0 || (x.AutoIMS.VehicleGrade ?? 0) > 0) + select x).OrderByDescending(x => x.ID); + + results.Items = await temp.IncludeAll().ToListAsync(); + results.Total = temp.Count(); + return results; + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.AuctionAssignments, c => c.ID == id).IncludeAll().FirstOrDefaultAsync(); + } + + public async Task> FindListByIdsAsync(IReadOnlyCollection idSet) + { + return await Queryable.Where(_context.AuctionAssignments, c => idSet.Contains(c.ID)).ToListAsync(); + } + + public async Task> FindByRepoIdAsync(int repoId) + { + return await _context.AuctionAssignments.Where(auctionAssignment => auctionAssignment.RepoId == repoId).IncludeAll().ToListAsync(); + } + + public async Task FindByByAuctionCustomerNoAndVin(string auctionCustomerNo, string vin) + { + return await Queryable + .Where(_context.AuctionAssignments, auctionAssignment => + auctionAssignment.AuctionCustomerNo == auctionCustomerNo && + auctionAssignment.CollateralVIN == vin && + auctionAssignment.IsActive == true).IncludeAll().FirstOrDefaultAsync(); + } + + public async Task AddAsync(AuctionAssignment auctionAssignment) + { + var result = await _context.AuctionAssignments.AddAsync(auctionAssignment); + result.Entity.Auction = Queryable.Where(_context.Auctions, a => a.AuctionID == result.Entity.AuctionId) + .FirstOrDefault(); + return result.Entity; + } + + public void Update(AuctionAssignment auctionAssignment) + { + _context.AuctionAssignments.Update(auctionAssignment); + } + + public void Remove(AuctionAssignment auctionAssignment) + { + _context.AuctionAssignments.Remove(auctionAssignment); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionHouseRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionHouseRepository.cs new file mode 100644 index 0000000..8cfa2a6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuctionHouseRepository.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class AuctionHouseRepository : BaseRepository, IAuctionHouseRepository + { + + public AuctionHouseRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.Auctions.ToListAsync(); + } + + public async Task AddAsync(AuctionHouse auction) + { + await _context.Auctions.AddAsync(auction); + } + + public async Task FindByIdAsync(string id) + { + return await Queryable.Where(_context.Auctions, c => c.AuctionID == id).FirstOrDefaultAsync(); + } + + public void Update(AuctionHouse auction) + { + _context.Auctions.Update(auction); + } + + public void Remove(AuctionHouse auction) + { + _context.Auctions.Remove(auction); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuditRecordRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuditRecordRepository.cs new file mode 100644 index 0000000..fa10ee5 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/AuditRecordRepository.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class AuditRecordRepository : BaseRepository, IAuditRecordRepository + { + public AuditRecordRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync(int filterPage, int filterLimit) + { + var results = new PagedResult(); + var skip = (filterPage - 1) * filterLimit; + + var temp = Queryable.OrderByDescending((from x in _context.AuditRecords + where x.DatePerformed > DateTime.Today.AddDays(-120) + select x), x => x.ID); + + results.Total = temp.Count(); + results.Items = await temp.Skip(skip).Take(filterLimit).ToListAsync(); + return results; + } + + public async Task AddAsync(AuditRecord auditRecord) + { + await _context.AuditRecords.AddAsync(auditRecord); + } + + public async Task FindByIdAsync(int id) + { + return await _context.AuditRecords.FindAsync(id); + } + + public void Update(AuditRecord auditRecord) + { + _context.AuditRecords.Update(auditRecord); + } + + public void Remove(AuditRecord auditRecord) + { + _context.AuditRecords.Remove(auditRecord); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncBatchRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncBatchRepository.cs new file mode 100644 index 0000000..d5e5a9c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncBatchRepository.cs @@ -0,0 +1,17 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.RepoApiModels.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Batch +{ + public interface IRepoSyncBatchRepository: ICrudRepository + { + Task> FindAllByRunInfoIdAsync(int id); + Task FindUncompletedByAuditId(string auditItemKey); + Task FindByBatchIdAndAuditIdAsync(int batchId, string auditItemKey); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncRunInfoRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncRunInfoRepository.cs new file mode 100644 index 0000000..7ba9543 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/IRepoSyncRunInfoRepository.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Batch +{ + public interface IRepoSyncRunInfoRepository: ICrudRepository + { + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncBatchRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncBatchRepository.cs new file mode 100644 index 0000000..6e29047 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncBatchRepository.cs @@ -0,0 +1,34 @@ +using LCU.API.Internal.RepoApi.Repos.Persistence; +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.RepoApiModels.Models; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Batch; + +public class RepoSyncBatchRepository : CrudRepository, IRepoSyncBatchRepository +{ + private readonly AppDbContext _context; + public RepoSyncBatchRepository(AppDbContext context) : base(context) { + _context = context; + } + + public async Task> FindAllByRunInfoIdAsync(int id) + { + return await _context.RepoSyncBatch.Where(repoSyncBatch => repoSyncBatch.RepoSyncRunInfoId == id).ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.RepoSyncBatch.Where(repoSyncBatch => repoSyncBatch.RepoSyncBatchId == id).FirstOrDefaultAsync(); + } + + public async Task FindUncompletedByAuditId(string auditItemKey) => + await _context.RepoSyncBatch + .Where(repoSyncBatch => repoSyncBatch.AuditItemKey == auditItemKey && repoSyncBatch.RepoSyncCompletionDateTime == null) + .FirstOrDefaultAsync(); + + public async Task FindByBatchIdAndAuditIdAsync(int batchId, string auditItemKey) => + await _context.RepoSyncBatch + .Where(repoSyncBatch => repoSyncBatch.RepoSyncBatchId == batchId && repoSyncBatch.AuditItemKey == auditItemKey) + .FirstOrDefaultAsync(); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncRunInfoRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncRunInfoRepository.cs new file mode 100644 index 0000000..072c66e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Batch/RepoSyncRunInfoRepository.cs @@ -0,0 +1,14 @@ +using LCU.API.Internal.RepoApi.Repos.Persistence; +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.RepoApiModels.Models; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Batch; + +public class RepoSyncRunInfoRepository : CrudRepository, IRepoSyncRunInfoRepository +{ + private readonly AppDbContext _context; + public RepoSyncRunInfoRepository(AppDbContext context) : base(context) { + _context = context; + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/CollateralRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/CollateralRepository.cs new file mode 100644 index 0000000..01cca9d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/CollateralRepository.cs @@ -0,0 +1,46 @@ +using LCU.API.Internal.RepoApi.Repos.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class CollateralRepository : BaseRepository, ICollateralRepository + { + + public CollateralRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.Collaterals.IncludeAll().ToListAsync(); + } + + public async Task AddAsync(Collateral collateral) + { + var result = await _context.Collaterals.AddAsync(collateral); + return result.Entity; + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.Collaterals, c => c.CollateralId == id).IncludeAll().FirstOrDefaultAsync(); + } + + public async Task> FindByRepoIdAsync(int repoId) + { + return await Queryable.Where(_context.Collaterals, c => c.RepoId == repoId).IncludeAll().ToListAsync(); + } + + public void Update(Collateral collateral) + { + _context.Collaterals.Update(collateral); + } + + public void Remove(Collateral collateral) + { + _context.Collaterals.Remove(collateral); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/EventAlertRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/EventAlertRepository.cs new file mode 100644 index 0000000..29ec646 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/EventAlertRepository.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using Serilog; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class EventAlertRepository : BaseRepository, IEventAlertRepository + { + private const string RepoStatus = "REPO"; + private const string O4RStatus = "O4R"; + private const int ClosedAccountCutoffDays = 90; + + private static List _nonRepeatingAlerts = new List + { + "Credit Adjustment Received", + "Debit Adjustment Received", + "Notice of Plan to Sell Not Found", + "Notice of Repossession Not Found", + "RTC Letter Not Found" + }; + + public EventAlertRepository(AppDbContext context) : base(context) + { + + } + + public async Task> ListAllAsync() + { + return await _context.EventAlerts.ToListAsync(); + } + + public async Task> ListUnreadAsync() + { + var result = from alert in _context.EventAlerts + join repo in _context.RepoAccounts + on alert.AccountNo equals repo.AccountNo + join activeStatus in _context.RepoManualStatuses + on repo.Status equals activeStatus.Description + where activeStatus.WatchForStatusChanges && alert.DateMarkedRead == null + select alert; + + return await result.Distinct().ToListAsync(); + } + + public async Task> ListWithinDateRangeAsync(DateTime start, DateTime end) + { + return await _context.EventAlerts.Where(e => e.DateAdded >= start && e.DateAdded <= end).ToListAsync(); + } + + public async Task> ListRepoNoAuctionAsync() + { + var closedDateCutoff = DateTime.Today.Subtract(TimeSpan.FromDays(ClosedAccountCutoffDays)); + var accountsInRepo = ( + from repo in _context.RepoAccounts + where repo.Status.Equals(RepoStatus) && (repo.DateClosed == null || repo.DateClosed > closedDateCutoff) + select repo); + + var accountsInRepoWithAuction = ( + from repo in accountsInRepo + join auctionAssignment in _context.AuctionAssignments + on repo.RepoId equals auctionAssignment.RepoId + where auctionAssignment.IsActive == true + select repo); + + var accountsNoAuction = await accountsInRepo.Except(accountsInRepoWithAuction).ToListAsync(); + + return accountsNoAuction.Select(repo => new EventAlert() + { + AccountNo = repo.AccountNo, + RepoId = repo.RepoId, + Message = "Repo No Auction" + }).Distinct(); + } + + public async Task> ListRepoNoCompanyAsync() + { + return await ListStatusNoCompanyAsync(RepoStatus); + } + + public async Task> ListO4rNoCompanyAsync() + { + return await ListStatusNoCompanyAsync(O4RStatus); + } + + public async Task> ListStatusNoCompanyAsync(string status) + { + var closedDateCutoff = DateTime.Today.Subtract(TimeSpan.FromDays(ClosedAccountCutoffDays)); + var result = from repo in _context.RepoAccounts + from collateral in _context.Collaterals.Where(c => c.RepoId == repo.RepoId) + from repoCompanySelection in _context.RepoCompanySelections.Where(x => x.CollateralId == collateral.CollateralId).DefaultIfEmpty() + where (repoCompanySelection == null || repoCompanySelection.RepoCompanyId == null) && repo.Status.Equals(status) && (repo.DateClosed == null || repo.DateClosed > closedDateCutoff) + select new EventAlert + { + AccountNo = repo.AccountNo, + RepoId = repo.RepoId, + Message = status + " No Repo Company" + }; + + var temp = await result.Distinct().ToListAsync(); + return temp; + } + + public async Task> ListReadyForRetitleAsync() + { + var result = from collateral in _context.Collaterals + join repo in _context.RepoAccounts + on collateral.RepoId equals repo.RepoId + join auctionAssignmentToCollateral in _context.AuctionAssignmentToCollaterals + on collateral.CollateralId equals auctionAssignmentToCollateral.CollateralId + join auctionAssignment in _context.AuctionAssignments + on auctionAssignmentToCollateral.AuctionAssignmentId equals auctionAssignment.ID + from data in Queryable.Where(_context.ImsAutoFileData, ims => auctionAssignment.ID == ims.AuctionAssignmentId || + (repo.RepoId == ims.RepoId && auctionAssignment.CollateralVIN == ims.VIN)) + where collateral.RetitledInLandmarkName == null && repo.Status.Equals(RepoStatus) && auctionAssignment.IsActive && repo.RedemptionExpiredDate < DateTime.Today + select new EventAlert + { + AccountNo = repo.AccountNo, + RepoId = repo.RepoId, + Message = "Ready For Retitle" + }; + + return await result.Distinct().ToListAsync(); + } + + public async Task> ListFloorUpdateFailedAsync() + { + var result = from autoFileData in _context.ImsAutoFileData + join repo in _context.RepoAccounts + on autoFileData.RepoId equals repo.RepoId + join collateral in _context.Collaterals + on repo.RepoId equals collateral.RepoId + join auctionAssignmentToCollateral in _context.AuctionAssignmentToCollaterals + on collateral.CollateralId equals auctionAssignmentToCollateral.CollateralId + join auctionAssignment in _context.AuctionAssignments + on auctionAssignmentToCollateral.AuctionAssignmentId equals auctionAssignment.ID + where ((auctionAssignment.HoldStatus ?? 0) == 0 || (auctionAssignment.HoldUntil ?? DateTime.Now) <= DateTime.Now) && + auctionAssignment.RedeemedDate == null && + autoFileData.SoldDate == null && + (auctionAssignment.FloorPriceResponseCode != null && auctionAssignment.FloorPriceResponseCode != 1) + select new EventAlert + { + AccountNo = repo.AccountNo, + RepoId = repo.RepoId, + Message = "Floor Update Failed" + }; + + return await result.Distinct().ToListAsync(); + } + + public async Task> ListFloorNeedsSettingAsync() + { + var result = from data in _context.ImsAutoFileData + join repo in _context.RepoAccounts + on data.RepoId equals repo.RepoId + join collateral in _context.Collaterals + on repo.RepoId equals collateral.RepoId + join auctionAssignmentToCollateral in _context.AuctionAssignmentToCollaterals + on collateral.CollateralId equals auctionAssignmentToCollateral.CollateralId + join auctionAssignment in _context.AuctionAssignments + on auctionAssignmentToCollateral.AuctionAssignmentId equals auctionAssignment.ID + where (auctionAssignment.FloorPrice ?? 0) == 0 && + ((auctionAssignment.HoldStatus ?? 0) == 0 || (auctionAssignment.HoldUntil ?? DateTime.Now) <= DateTime.Now) && + auctionAssignment.RedeemedDate == null && + data.SoldDate == null && + data.ScheduledSaleDate != null && + data.ScheduledSaleDate < DateTime.Now.AddDays(3) && + repo.DateClosed == null + select new EventAlert + { + AccountNo = repo.AccountNo, + RepoId = repo.RepoId, + Message = "Floor Set Required" + }; + + return await result.Distinct().ToListAsync(); + } + + public async Task AddAsync(EventAlert eventAlert) + { + if (eventAlert.SubMessage != null && eventAlert.SubMessage.Length > 499) + { + eventAlert.SubMessage = eventAlert.SubMessage.Substring(0, 499); + } + + if (!IsDuplicateAlert(eventAlert)) + { + await _context.EventAlerts.AddAsync(eventAlert); + } + else + { + throw new DuplicateResourceException("EventAlert"); + } + } + + public async Task> AddRangeAsync(IEnumerable eventAlerts) + { + List filteredEventAlerts = new List(); + + foreach (EventAlert eventAlert in eventAlerts) + { + if (eventAlert.SubMessage != null && eventAlert.SubMessage.Length > 499) + { + eventAlert.SubMessage = eventAlert.SubMessage.Substring(0, 499); + } + + if (!IsDuplicateAlert(eventAlert)) + { + filteredEventAlerts.Add(eventAlert); + } + else + { + Log.Debug($"EventAlertRepository: Valid Alert Already Exists eventAlert: {@eventAlert}." + + $" Will Continue without adding this to Event Alerts Context.", eventAlerts, eventAlert + ); + } + } + + await _context.EventAlerts.AddRangeAsync(filteredEventAlerts); + return filteredEventAlerts; + } + + public bool IsDuplicateAlert(EventAlert eventAlert) + { + var duplicateAlerts = ( + from alert in _context.EventAlerts + where alert.AccountNo == eventAlert.AccountNo && + alert.Message == eventAlert.Message && + ( + alert.DateMarkedRead == null || + alert.DateMarkedRead > DateTime.Now.AddHours(-24) || + _nonRepeatingAlerts.Contains(alert.Message) + ) + select alert + ).ToList(); + + return (duplicateAlerts.Any()); + } + + public async Task FindByIdAsync(int id) + { + return await _context.EventAlerts.FindAsync(id); + } + + public void Update(EventAlert category) + { + _context.EventAlerts.Update(category); + } + + public void Remove(EventAlert category) + { + _context.EventAlerts.Remove(category); + } + + public async Task> ListUnreadByAccountNoAsync(string accountNo) + { + var results = _context.EventAlerts.Where(x => x.AccountNo == accountNo && x.DateMarkedRead == null); + return await results.ToListAsync(); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ExternalUserRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ExternalUserRepository.cs new file mode 100644 index 0000000..e04538f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ExternalUserRepository.cs @@ -0,0 +1,41 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class ExternalUserRepository : BaseRepository, IExternalUserRepository + { + public ExternalUserRepository(AppDbContext context) : base(context) { } + + public async Task> ListAsync() + { + var result = await _context.ExternalUsers.AsNoTracking().ToListAsync(); + return result; + } + + public async Task FindByReferenceIdAsync(int referenceId) + { + return await _context.ExternalUsers.AsNoTracking().Where(user => user.ReferenceId == referenceId).FirstOrDefaultAsync(); + } + + public async Task SynchronizeSet(IReadOnlyCollection externalUsers) + { + foreach (var user in externalUsers) + { + var existing = await FindByReferenceIdAsync(user.ReferenceId); + if (existing != null) + { + user.Id = existing.Id; + _context.Entry(user).State = EntityState.Modified; + _context.ExternalUsers.Update(user); + } + else + { + await _context.ExternalUsers.AddAsync(user); + } + } + } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentNoteRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentNoteRepository.cs new file mode 100644 index 0000000..f0e3dc9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentNoteRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IAuctionAssignmentNoteRepository + { + Task> ListAllAsync(int filterPage, int filterLimit); + Task FindByIdAsync(int id); + Task> FindConflictingNotes(DateTime written, string text, string writer); + Task AddAsync(AuctionAssignmentNote auctionAssignmentNote); + void Update(AuctionAssignmentNote auctionAssignmentNote); + void Remove(AuctionAssignmentNote auctionAssignmentNote); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentRepository.cs new file mode 100644 index 0000000..70fe195 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionAssignmentRepository.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IAuctionAssignmentRepository + { + Task> ListAllAsync(int filterPage, int filterLimit); + Task> ListReadyForFloorPriceAsync(); + Task FindByIdAsync(int id); + Task> FindByRepoIdAsync(int repoId); + Task FindByByAuctionCustomerNoAndVin(string auctionCustomerNo, string vin); + Task AddAsync(AuctionAssignment auctionAssignment); + void Update(AuctionAssignment auctionAssignment); + void Remove(AuctionAssignment auctionAssignment); + Task> FindListByIdsAsync(IReadOnlyCollection idSet); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionHouseRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionHouseRepository.cs new file mode 100644 index 0000000..e1cb190 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuctionHouseRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IAuctionHouseRepository + { + Task> ListAllAsync(); + Task AddAsync(AuctionHouse assignment); + Task FindByIdAsync(string id); + void Update(AuctionHouse assignment); + void Remove(AuctionHouse assignment); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuditRecordRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuditRecordRepository.cs new file mode 100644 index 0000000..a944074 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IAuditRecordRepository.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IAuditRecordRepository + { + Task> ListAllAsync(int filterPage, int filterLimit); + Task AddAsync(AuditRecord auditRecord); + Task FindByIdAsync(int id); + void Update(AuditRecord auditRecord); + void Remove(AuditRecord auditRecord); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ICollateralRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ICollateralRepository.cs new file mode 100644 index 0000000..b67e4ae --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ICollateralRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface ICollateralRepository + { + Task> ListAllAsync(); + Task AddAsync(Collateral collateral); + Task FindByIdAsync(int id); + Task> FindByRepoIdAsync(int repoId); + void Update(Collateral collateral); + void Remove(Collateral collateral); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IEventAlertRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IEventAlertRepository.cs new file mode 100644 index 0000000..cb60f47 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IEventAlertRepository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IEventAlertRepository + { + Task> ListAllAsync(); + Task> ListUnreadAsync(); + Task> ListWithinDateRangeAsync(DateTime start, DateTime end); + Task> ListRepoNoAuctionAsync(); + Task> ListRepoNoCompanyAsync(); + Task> ListO4rNoCompanyAsync(); + Task> ListReadyForRetitleAsync(); + Task> ListFloorUpdateFailedAsync(); + Task> ListFloorNeedsSettingAsync(); + Task> ListUnreadByAccountNoAsync(string accountNo); + Task AddAsync(EventAlert eventAlert); + Task> AddRangeAsync(IEnumerable eventAlerts); + Task FindByIdAsync(int id); + void Update(EventAlert eventAlert); + void Remove(EventAlert eventAlert); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IExternalUserRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IExternalUserRepository.cs new file mode 100644 index 0000000..07e14ee --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IExternalUserRepository.cs @@ -0,0 +1,11 @@ +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IExternalUserRepository + { + Task> ListAsync(); + Task FindByReferenceIdAsync(int referenceId); + Task SynchronizeSet(IReadOnlyCollection externalUsers); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IImsAutoFileDataRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IImsAutoFileDataRepository.cs new file mode 100644 index 0000000..f321521 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IImsAutoFileDataRepository.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IImsAutoFileDataRepository + { + Task> ListAllAsync(); + Task AddAsync(ImsAutoFileData assignment); + Task FindByIdAsync(string vin, int repoId); + Task FindByAuctionAssignmentIdAsync(int auctionAssignmentId); + Task GetNonConformingImsFileRecord(string vin, string auctionCustomerNo); + void Update(ImsAutoFileData autoImsAutoFileData); + void Remove(ImsAutoFileData autoImsAutoFileData); + Task> GetImsSoldRecordsForPeriod(DateTime lowDateTime, DateTime highDateTime); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ILetterRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ILetterRepository.cs new file mode 100644 index 0000000..81d1e63 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ILetterRepository.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface ILetterRepository + { + Task> ListAsync(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IProcessingReceiptRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IProcessingReceiptRepository.cs new file mode 100644 index 0000000..ef380fe --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IProcessingReceiptRepository.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IProcessingReceiptRepository + { + Task> FindReceiptsAsync(string accountNo, + string accountId = "", string caseId = ""); + Task> GetReceiptsByAccountNoAsync(string accountNo); + Task> GetRetryListAsync(); + Task AddOrUpdateAsync(ProcessingReceipt receipt); + Task SaveAllAsync(IReadOnlyCollection receipts); + bool IsDuplicateReceipt(ProcessingReceipt newReceipt); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRefundStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRefundStatusRepository.cs new file mode 100644 index 0000000..4a889bf --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRefundStatusRepository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRefundStatusRepository + { + Task> ListAsync(); + Task FindByRepoIdAsync(int repoId); + Task AddAsync(RefundStatus refundStatus); + void Update(RefundStatus refundStatus); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoAccountRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoAccountRepository.cs new file mode 100644 index 0000000..30635b9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoAccountRepository.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.Library.Internal.RepoApiModels.Models.Requests; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRepoAccountRepository + { + Task> ListAsync(List filterRepoIdList, int filterPage, int filterLimit, string status = null, string auctionHouseId = null, int? repoCompanyId = null, bool? onlyOpenAccounts = null); + Task> ListByAuctionVinAsync(string unformattedVin); + Task> FindByCurrentStatus(string currentStatus); + Task> FindByStatusChangeInPeriod(string status, DateTime lowDateTime, DateTime highDateTime, string previousStatus = null); + Task AddAsync(RepoAccount repoAccount); + Task FindByIdAsync(int id); + Task> FindByRepoIdAsync(List filterRepoIdList); + Task> SearchRepoAccounts(string searchPhrase, int maxLimit); + Task> FindByAccountNoAsync(string accountNo); + Task FindByAuctionAssignmentIdAsync(int id); + Task> GetCurrentAccountsOnHoldAtAuction(); + void Update(RepoAccount eventAlert); + void Remove(RepoAccount eventAlert); + Task> GetItemsForProcessing(IReadOnlyCollection watchStatusList); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanyRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanyRepository.cs new file mode 100644 index 0000000..63231d7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanyRepository.cs @@ -0,0 +1,18 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRepoCompanyRepository + { + Task> ListAsync(); + Task AddAsync(RepoCompany repoCompany); + Task FindByIdAsync(int id); + void Update(RepoCompany repoCompany); + void Remove(RepoCompany repoCompany); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanySelectionRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanySelectionRepository.cs new file mode 100644 index 0000000..e9c3b69 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoCompanySelectionRepository.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRepoCompanySelectionRepository + { + Task> ListAllAsync(); + Task AddAsync(RepoCompanySelection repoCompanySelection); + Task FindByIdAsync(int id); + Task> FindByCollateralIdAsync(int collateralId); + void Update(RepoCompanySelection repoCompanySelection); + void Remove(RepoCompanySelection repoCompanySelection); + + void SaveRange(IList repoCompanySelections); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoManualStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoManualStatusRepository.cs new file mode 100644 index 0000000..4ebb2f6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoManualStatusRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRepoManualStatusRepository + { + Task> ListAllAsync(); + Task> ListWatchStatusesAsync(); + Task AddAsync(RepoManualStatus repoManualStatus); + Task FindByIdAsync(int id); + void Update(RepoManualStatus repoManualStatus); + void Remove(RepoManualStatus repoManualStatus); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoStatusChangeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoStatusChangeRepository.cs new file mode 100644 index 0000000..e994b7a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IRepoStatusChangeRepository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IRepoStatusChangeRepository + { + Task> ListAsync(); + Task> ListForAccountAsync(int repoAccountId); + Task AddAsync(RepoStatusChange statusChange); + Task FindByIdAsync(int id); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ISavedLocationRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ISavedLocationRepository.cs new file mode 100644 index 0000000..560d217 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ISavedLocationRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface ISavedLocationRepository + { + Task> ListAsync(); + Task AddAsync(SavedLocation savedLocation); + Task FindByIdAsync(int id); + void Update(SavedLocation savedLocation); + void Remove(SavedLocation savedLocation); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IUnitOfWork.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IUnitOfWork.cs new file mode 100644 index 0000000..42c538c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IUnitOfWork.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IUnitOfWork + { + Task CompleteAsync(); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IVehicleFloorPricingParametersRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IVehicleFloorPricingParametersRepository.cs new file mode 100644 index 0000000..678edaa --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/IVehicleFloorPricingParametersRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public interface IVehicleFloorPricingParametersRepository + { + Task> ListAllAsync(); + Task AddAsync(VehicleFloorPricingParameters Collateral); + Task FindByIdAsync(int id); + void Update(VehicleFloorPricingParameters Collateral); + void Remove(VehicleFloorPricingParameters Collateral); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ImsAutoFileDataRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ImsAutoFileDataRepository.cs new file mode 100644 index 0000000..b440f47 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ImsAutoFileDataRepository.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class ImsAutoFileDataRepository : BaseRepository, IImsAutoFileDataRepository + { + public ImsAutoFileDataRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.ImsAutoFileData.ToListAsync(); + } + + public async Task AddAsync(ImsAutoFileData assignment) + { + await _context.ImsAutoFileData.AddAsync(assignment); + } + + public async Task FindByIdAsync(string vin, int repoId) + { + return await _context.ImsAutoFileData.Where(x => x.VIN == vin && x.RepoId == repoId).FirstOrDefaultAsync(); + } + + public async Task FindByAuctionAssignmentIdAsync(int auctionAssignmentId) + { + return await _context.ImsAutoFileData.Where(x => x.AuctionAssignmentId == auctionAssignmentId) + .FirstOrDefaultAsync(); + } + + public async Task GetNonConformingImsFileRecord(string vin, string auctionCustomerNo) + { + ImsAutoFileData imsAutoFile = null; + + // Allow for vehicle brought back to auction after sale. it happens! + DateTime withinThirtyDays = DateTime.Now.AddDays(-30); + imsAutoFile = await _context.ImsAutoFileData.Where(x => + (x.VIN.Trim() == vin && x.AuctionCustomerNo == auctionCustomerNo + && (x.SoldDate == null || x.SoldDate >= withinThirtyDays))).FirstOrDefaultAsync(); + + return imsAutoFile; + } + + public void Update(ImsAutoFileData autoImsAutoFileData) + { + _context.ImsAutoFileData.Update(autoImsAutoFileData); + } + + public void Remove(ImsAutoFileData autoImsAutoFileData) + { + _context.ImsAutoFileData.Remove(autoImsAutoFileData); + } + + public async Task> GetImsSoldRecordsForPeriod(DateTime lowDateTime, + DateTime highDateTime) + { + return await _context.ImsAutoFileData + .Where(x => x.SoldDate != null && x.SoldDate >= lowDateTime && x.SoldDate <= highDateTime) + .ToListAsync(); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/LetterRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/LetterRepository.cs new file mode 100644 index 0000000..92407fc --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/LetterRepository.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class LetterRepository : BaseRepository, ILetterRepository + { + public LetterRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAsync() + { + return await _context.Letters.ToListAsync(); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ProcessingReceiptRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ProcessingReceiptRepository.cs new file mode 100644 index 0000000..6c414a0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/ProcessingReceiptRepository.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class ProcessingReceiptRepository : BaseRepository, IProcessingReceiptRepository + { + private readonly IReadOnlyCollection _retryOutcomes = new List() + { + RepoProcessingResult.Error, RepoProcessingResult.MissingRequirement + }; + + public ProcessingReceiptRepository(AppDbContext context) : base(context) + { + } + + public async Task> FindReceiptsAsync(string accountNo, + string accountId = "", string caseId = "") + { + return await _context.ProcessingReceipts.Where(receipt => receipt.AccountNo == accountNo + && receipt.AccountId == accountId && + receipt.CaseId == caseId).AsNoTracking().ToListAsync(); + } + + public async Task> GetReceiptsByAccountNoAsync(string accountNo) + { + return await _context.ProcessingReceipts.Where(receipt => receipt.AccountNo == accountNo).AsNoTracking().ToListAsync(); + } + + public async Task> GetRetryListAsync() + { + return await _context.ProcessingReceipts.Where(receipt => _retryOutcomes.Contains(receipt.Outcome)) + .AsNoTracking().ToListAsync(); + } + + public async Task AddOrUpdateAsync(ProcessingReceipt receipt) + { + if (receipt.Message != null && receipt.Message.Length > 124) + { + receipt.Message = receipt.Message.Substring(0, 124); + } + + if (receipt.QueuedBy != null && receipt.QueuedBy.Length > 39) + { + receipt.QueuedBy = receipt.QueuedBy.Substring(0, 39); + } + + if (!IsDuplicateReceipt(receipt)) + { + await _context.ProcessingReceipts.AddAsync(receipt); + } + else + { + _context.Entry(receipt).State = EntityState.Modified; + _context.ProcessingReceipts.Update(receipt); + } + } + + public async Task SaveAllAsync(IReadOnlyCollection receipts) + { + var saveAllReceipts = receipts.Select(AddOrUpdateAsync).ToList(); + await Task.WhenAll(saveAllReceipts); + } + + public bool IsDuplicateReceipt(ProcessingReceipt newReceipt) + { + var duplicateReceipts = ( + from existingReceipt in _context.ProcessingReceipts + where existingReceipt.AccountNo == newReceipt.AccountNo + && existingReceipt.AccountId == newReceipt.AccountId + && existingReceipt.CaseId == newReceipt.CaseId + select existingReceipt + ).AsNoTracking().ToList(); + + return (duplicateReceipts.Count() != 0); + } + + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RefundStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RefundStatusRepository.cs new file mode 100644 index 0000000..1b38db7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RefundStatusRepository.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RefundStatusRepository : BaseRepository, IRefundStatusRepository + { + public RefundStatusRepository(AppDbContext context) : base(context) {} + + public async Task> ListAsync() + { + return await _context.RefundStatuses.ToListAsync(); + } + + public async Task FindByRepoIdAsync(int repoId) + { + return await _context.RefundStatuses.Where(refundStatus => refundStatus.RepoId == repoId).FirstOrDefaultAsync(); + } + + public async Task AddAsync(RefundStatus refundStatus) + { + var result = await _context.RefundStatuses.AddAsync(refundStatus); + return result.Entity; + } + + public void Update(RefundStatus refundStatus) + { + _context.RefundStatuses.Update(refundStatus); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoAccountRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoAccountRepository.cs new file mode 100644 index 0000000..85e5694 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoAccountRepository.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using Microsoft.EntityFrameworkCore; +using Serilog; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RepoAccountRepository : BaseRepository, IRepoAccountRepository + { + public RepoAccountRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAsync( + List filterAccountNoList, + int filterPage, + int filterLimit, + string status, + string auctionHouseId, + int? repoCompanyId, + bool? onlyOpenAccounts) + { + var results = new PagedResult(); + var skip = (filterPage - 1) * filterLimit; + + + var accounts = _context.RepoAccounts.Select(account => account); + + // Filter By Repo Ids? + if (filterAccountNoList != null && filterAccountNoList.Any()) + { + accounts = accounts.Where(account => filterAccountNoList.Contains(account.RepoId)); + } + + // Filter By Status? + if (!string.IsNullOrEmpty(status)) + { + accounts = accounts.Where(account => account.Status.ToLower() == status.ToLower()); + } + + // Filter By Auction House? + if (!string.IsNullOrEmpty(auctionHouseId)) + { + accounts = accounts.Join(_context.AuctionAssignments.AsEnumerable(), account => account.RepoId, auction => auction.RepoId, + (account, auction) => new { AuctionAssignment = auction, RepoAccount = account }) + .Where(joined => joined.AuctionAssignment.IsActive && joined.AuctionAssignment.AuctionId == auctionHouseId) + .Select(result => result.RepoAccount); + } + + // Filter By Repo Company? + if (repoCompanyId != null) + { + accounts = accounts.Join(_context.Collaterals.AsEnumerable(), account => account.RepoId, collateral => collateral.RepoId, + (account, collateral) => new { RepoAccount = account, Collateral = collateral }) + .Join(_context.RepoCompanySelections.AsEnumerable(), accountCollateral => accountCollateral.Collateral.CollateralId, + repocompany => repocompany.CollateralId, + (accountCollateral, repocompany) => new { accountCollateral.RepoAccount, accountCollateral.Collateral, RepoCompany = repocompany }) + .Where(joined => joined.RepoCompany.RepoCompanyId == repoCompanyId) + .Select(result => result.RepoAccount); + } + + // Filter Out Closed Accounts? + if (onlyOpenAccounts == true) + { + accounts = accounts.Where(account => !account.DateClosed.HasValue); + } + + accounts = accounts.Distinct(); + + results.Total = accounts.Count(); + results.Items = await accounts.Skip(skip).Take(filterLimit).IncludeAll().ToListAsync(); + return results; + } + + public async Task> FindByRepoIdAsync(List filterRepoIdList) + { + var accounts = await Queryable.OrderByDescending((from account in _context.RepoAccounts + where filterRepoIdList.Count == 0 || filterRepoIdList.Contains(account.RepoId.ToString()) + select account), x => x.AccountNo).IncludeAll().ToListAsync(); + + return accounts; + } + + public async Task> ListByAuctionVinAsync(string unformattedVin) + { + string vin = unformattedVin.Trim().ToUpper(); + + List repos = await (from account in _context.RepoAccounts + join collateral in _context.Collaterals on account.RepoId equals collateral.RepoId + where GetFormattedVIN(collateral.Vin) == vin + select account).ToListAsync(); + + return repos; + } + + public async Task> FindByStatusChangeInPeriod( + string status, + DateTime lowDateTime, + DateTime highDateTime, + string previousStatus = null) + { + var repos = await ( + from account in _context.RepoAccounts + join statusChange in _context.RepoStatusChanges on account.RepoId equals statusChange.RepoId + where + string.Equals(account.Status, status, StringComparison.CurrentCultureIgnoreCase) && + ( + previousStatus != null + ? string.Equals(statusChange.StatusFrom, previousStatus, StringComparison.CurrentCultureIgnoreCase) + : true + ) && + string.Equals(statusChange.StatusTo, status, StringComparison.CurrentCultureIgnoreCase) && + statusChange.ChangedDate >= lowDateTime && statusChange.ChangedDate <= highDateTime + select account) + .Distinct() + .IncludeAll() + .ToListAsync(); + + return repos; + } + + public async Task AddAsync(RepoAccount repo) + { + var result = await _context.RepoAccounts.AddAsync(repo); + return result.Entity; + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.RepoAccounts, x => x.RepoId == id) + .IncludeAll() + .FirstOrDefaultAsync(); + } + + public async Task> FindByAccountNoAsync(string accountNo) + { + var result = await Queryable.Where(_context.RepoAccounts, x => x.AccountNo == accountNo) + .IncludeAll().AsNoTracking().ToListAsync(); + return result; + } + + public async Task FindByAuctionAssignmentIdAsync(int id) + { + RepoAccount repo = await ( + from account in _context.RepoAccounts + join collateral in _context.Collaterals on account.RepoId equals collateral.RepoId + join auctionAssignmentToCollateral in _context.AuctionAssignmentToCollaterals on collateral.CollateralId equals auctionAssignmentToCollateral + .CollateralId + where auctionAssignmentToCollateral.AuctionAssignmentId == id + select account).FirstOrDefaultAsync(); + + return repo; + } + + public static string GetFormattedVIN(string vin) + { + return vin.Trim().PadLeft(17, '0').ToUpper(); + } + + public void Update(RepoAccount account) + { + _context.Entry(account).State = EntityState.Modified; + _context.RepoAccounts.Update(account); + } + + public void Remove(RepoAccount account) + { + _context.RepoAccounts.Remove(account); + } + + public async Task> GetItemsForProcessing(IReadOnlyCollection watchStatusList) + { + var accountList = await _context.RepoAccounts.Where(account => + account.DateClosed == null && watchStatusList.Contains(account.Status.ToUpper().Trim())).ToListAsync(); + + var list = accountList.Select(x => new RepoAccountProcessingRequest() + { + AccountId = x.ReferenceId.HasValue ? x.ReferenceId.ToString() : "", + CaseId = x.ReferenceCaseId.HasValue ? x.ReferenceCaseId.ToString() : "", + AccountNo = x.AccountNo + }); + return list.ToList(); + } + + public async Task> SearchRepoAccounts(string searchPhrase, int maxLimit) + { + var updatedSearchPhrase = searchPhrase.Trim().ToLower(); + var entities = ( + from account in _context.RepoAccounts + join collateral in _context.Collaterals on account.RepoId equals collateral.RepoId + join member in _context.Borrowers on account.RepoId equals member.RepoId + where + account.AccountNo.ToLower().Contains(updatedSearchPhrase) || + collateral.Vin.ToLower().Contains(updatedSearchPhrase) || + member.IndividualName.ToLower().Contains(updatedSearchPhrase) + select account + ); + + var itemsFound = entities.Count(); + if (itemsFound > maxLimit) + { + throw new MaxItemException(itemsFound); + } + + return await entities + .IncludeAll().Distinct() /* distinct because repo can have multiple collaterals, borrowers */ + .ToListAsync(); + } + + public async Task> FindByCurrentStatus(string currentStatus) + { + return await _context.RepoAccounts.Where(repoAccount => + repoAccount.Status.ToLower() == currentStatus.ToLower()).IncludeAll().ToListAsync(); + } + + public async Task> GetCurrentAccountsOnHoldAtAuction() + { + var noHoldStatus = 0; + return await ( + from repo in _context.RepoAccounts + join auctionAssignment in _context.AuctionAssignments on repo.RepoId equals auctionAssignment.RepoId + where + auctionAssignment.HoldStatus != noHoldStatus && + DateTime.Now < auctionAssignment.HoldUntil + select repo).Distinct().IncludeAll().ToListAsync(); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanyRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanyRepository.cs new file mode 100644 index 0000000..063ca43 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanyRepository.cs @@ -0,0 +1,45 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RepoCompanyRepository : BaseRepository, IRepoCompanyRepository + { + public RepoCompanyRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAsync() + { + return await _context.RepoCompanies.ToListAsync(); + } + + public async Task AddAsync(RepoCompany repoCompany) + { + var result = await _context.RepoCompanies.AddAsync(repoCompany); + return result.Entity; + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.RepoCompanies, x => x.ID == id).FirstOrDefaultAsync(); + } + + + public void Update(RepoCompany repoCompany) + { + _context.RepoCompanies.Update(repoCompany); + } + + public void Remove(RepoCompany repoCompany) + { + _context.RepoCompanies.Remove(repoCompany); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanySelectionRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanySelectionRepository.cs new file mode 100644 index 0000000..8b9dd8c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoCompanySelectionRepository.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RepoCompanySelectionRepository : BaseRepository, IRepoCompanySelectionRepository + { + public RepoCompanySelectionRepository(AppDbContext context) : base(context) {} + + public async Task AddAsync(RepoCompanySelection repoCompanySelection) + { + repoCompanySelection.Id = null; + repoCompanySelection.DateTimeCreated = DateTime.Now; + await _context.RepoCompanySelections.AddAsync(repoCompanySelection); + } + + public async Task FindByIdAsync(int id) + { + return await _context.RepoCompanySelections.Where(repoCompanySelection => repoCompanySelection.Id == id).FirstOrDefaultAsync(); + } + + public async Task> FindByCollateralIdAsync(int collateralId) + { + return await _context.RepoCompanySelections.Where(repoCompanySelection => repoCompanySelection.CollateralId == collateralId).ToListAsync(); + } + + public async Task> ListAllAsync() + { + return await _context.RepoCompanySelections.ToListAsync(); + } + + public void Remove(RepoCompanySelection repoCompanySelection) + { + _context.RepoCompanySelections.Remove(repoCompanySelection); + } + + public void Update(RepoCompanySelection repoCompanySelection) + { + _context.RepoCompanySelections.Update(repoCompanySelection); + } + + public void SaveRange(IList repoCompanySelections) + { + _context.RepoCompanySelections.UpdateRange(repoCompanySelections); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoManualStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoManualStatusRepository.cs new file mode 100644 index 0000000..90ec133 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoManualStatusRepository.cs @@ -0,0 +1,62 @@ +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RepoManualStatusRepository : BaseRepository, IRepoManualStatusRepository + { + public RepoManualStatusRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.RepoManualStatuses.ToListAsync(); + } + + public async Task> ListWatchStatusesAsync() + { + var statusSet = await (from status in _context.RepoManualStatuses + where status.WatchForStatusChanges == true + select status).ToListAsync(); + + return statusSet.Select(x => x.Description).ToList() as IReadOnlyCollection; + } + + public async Task AddAsync(RepoManualStatus repoManualStatus) + { + // Only Create the Alert if an identical one doesn't already exist + var duplicateStatus = ( + from status in _context.RepoManualStatuses + where status.Description.ToUpper().Trim() == repoManualStatus.Description.ToUpper().Trim() + select status + ); + + if (!duplicateStatus.Any()) + { + await _context.RepoManualStatuses.AddAsync(repoManualStatus); + } + else + { + throw new DuplicateResourceException("RepoManualStatus"); + } + } + + public async Task FindByIdAsync(int id) + { + return await _context.RepoManualStatuses.FindAsync(id); + } + + public void Update(RepoManualStatus repoManualStatus) + { + _context.RepoManualStatuses.Update(repoManualStatus); + } + + public void Remove(RepoManualStatus repoManualStatus) + { + _context.RepoManualStatuses.Remove(repoManualStatus); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoStatusChangeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoStatusChangeRepository.cs new file mode 100644 index 0000000..e9c12b7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/RepoStatusChangeRepository.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class RepoStatusChangeRepository : BaseRepository, IRepoStatusChangeRepository + { + public RepoStatusChangeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAsync() + { + return await _context.RepoStatusChanges.ToListAsync(); + } + + public async Task> ListForAccountAsync(int repoAccountId) + { + return await Queryable.Where(_context.RepoStatusChanges, p => p.RepoId == repoAccountId).ToListAsync(); + } + + public async Task AddAsync(RepoStatusChange statusChange) + { + var result = await _context.RepoStatusChanges.AddAsync(statusChange); + return result.Entity; + } + + public async Task FindByIdAsync(int id) + { + return await _context.RepoStatusChanges.FindAsync(id); + } + + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/SavedLocationRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/SavedLocationRepository.cs new file mode 100644 index 0000000..753d222 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/SavedLocationRepository.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class SavedLocationRepository : BaseRepository, ISavedLocationRepository + { + public SavedLocationRepository(AppDbContext context) : base(context) { } + + public async Task> ListAsync() + { + return await _context.SavedLocations.ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.SavedLocations.Where(savedLocation => savedLocation.ID == id).FirstOrDefaultAsync(); + } + + public async Task AddAsync(SavedLocation savedLocation) + { + var result = await _context.SavedLocations.AddAsync(savedLocation); + return result.Entity; + } + + public void Update(SavedLocation savedLocation) + { + _context.SavedLocations.Update(savedLocation); + } + + public void Remove(SavedLocation savedLocation) + { + _context.Remove(savedLocation); + } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AccountTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AccountTypeRepository.cs new file mode 100644 index 0000000..678ff9c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AccountTypeRepository.cs @@ -0,0 +1,39 @@ +using LCU.API.Internal.RepoApi.Repos.Persistence; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class AccountTypeRepository : BaseRepository, IAccountTypeRepository + { + + public AccountTypeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.AccountTypeCodes.ToListAsync(); + } + + public async Task AddAsync(AccountType accountTypeCodes) + { + await _context.AccountTypeCodes.AddAsync(accountTypeCodes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.AccountTypeCodes.Where(c => c.ID == id).FirstOrDefaultAsync(); + } + + public void Update(AccountType category) + { + _context.AccountTypeCodes.Update(category); + } + + public void Remove(AccountType category) + { + _context.AccountTypeCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AllowedStatusChangeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AllowedStatusChangeRepository.cs new file mode 100644 index 0000000..5f9bdb1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/AllowedStatusChangeRepository.cs @@ -0,0 +1,40 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class AllowedStatusChangeRepository : BaseRepository, IAllowedStatusChangeRepository + { + + public AllowedStatusChangeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.AllowedStatusChanges.ToListAsync(); + } + + public async Task AddAsync(AllowedStatusChange allowedStatusChanges) + { + await _context.AllowedStatusChanges.AddAsync(allowedStatusChanges); + } + + public async Task FindByIdAsync(int id) + { + return await _context.AllowedStatusChanges.Where(c => c.ID == id).FirstOrDefaultAsync(); + } + + public void Update(AllowedStatusChange category) + { + _context.AllowedStatusChanges.Update(category); + } + + public void Remove(AllowedStatusChange category) + { + _context.AllowedStatusChanges.Remove(category); + } + } + +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/BodyStyleRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/BodyStyleRepository.cs new file mode 100644 index 0000000..c98a68a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/BodyStyleRepository.cs @@ -0,0 +1,39 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class BodyStyleRepository : BaseRepository, IBodyStyleRepository + { + + public BodyStyleRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.BodyStyleCodes.ToListAsync(); + } + + public async Task AddAsync(BodyStyle bodyStyleCodes) + { + await _context.BodyStyleCodes.AddAsync(bodyStyleCodes); + } + + public async Task FindByIdAsync(string id) + { + return await _context.BodyStyleCodes.Where(c => c.Code.Equals(id)).FirstOrDefaultAsync(); + } + + public void Update(BodyStyle category) + { + _context.BodyStyleCodes.Update(category); + } + + public void Remove(BodyStyle category) + { + _context.BodyStyleCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CleaningLevelRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CleaningLevelRepository.cs new file mode 100644 index 0000000..68ff2cd --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CleaningLevelRepository.cs @@ -0,0 +1,39 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class CleaningLevelRepository : BaseRepository, ICleaningLevelRepository + { + + public CleaningLevelRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.CleaningLevelCodes.ToListAsync(); + } + + public async Task AddAsync(CleaningLevel cleaningLevelCodes) + { + await _context.CleaningLevelCodes.AddAsync(cleaningLevelCodes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.CleaningLevelCodes.Where(c => c.Code == id).FirstOrDefaultAsync(); + } + + public void Update(CleaningLevel category) + { + _context.CleaningLevelCodes.Update(category); + } + + public void Remove(CleaningLevel category) + { + _context.CleaningLevelCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeRepository.cs new file mode 100644 index 0000000..585debc --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeRepository.cs @@ -0,0 +1,44 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class CollateralTypeRepository : BaseRepository, ICollateralTypeRepository + { + + public CollateralTypeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.CollateralTypes.ToListAsync(); + } + + public async Task AddAsync(CollateralType collateralTypes) + { + await _context.CollateralTypes.AddAsync(collateralTypes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.CollateralTypes.Where(c => c.ID == id).FirstOrDefaultAsync(); + } + + public void Update(CollateralType category) + { + _context.CollateralTypes.Update(category); + } + + public void Remove(CollateralType category) + { + _context.CollateralTypes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeToVehicleTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeToVehicleTypeRepository.cs new file mode 100644 index 0000000..55712b1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/CollateralTypeToVehicleTypeRepository.cs @@ -0,0 +1,44 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class CollateralTypeToVehicleTypeRepository : BaseRepository, ICollateralTypeToVehicleTypeRepository + { + + public CollateralTypeToVehicleTypeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.CollateralTypesToVehicleTypes.ToListAsync(); + } + + public async Task AddAsync(CollateralTypeToVehicleType collateralTypesToVehicleTypes) + { + await _context.CollateralTypesToVehicleTypes.AddAsync(collateralTypesToVehicleTypes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.CollateralTypesToVehicleTypes.Where(c => c.ID == id).FirstOrDefaultAsync(); + } + + public void Update(CollateralTypeToVehicleType category) + { + _context.CollateralTypesToVehicleTypes.Update(category); + } + + public void Remove(CollateralTypeToVehicleType category) + { + _context.CollateralTypesToVehicleTypes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/HoldStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/HoldStatusRepository.cs new file mode 100644 index 0000000..b4b072a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/HoldStatusRepository.cs @@ -0,0 +1,39 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class HoldStatusRepository : BaseRepository, IHoldStatusRepository + { + + public HoldStatusRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.HoldStatusCodes.ToListAsync(); + } + + public async Task AddAsync(HoldStatus holdStatusCodes) + { + await _context.HoldStatusCodes.AddAsync(holdStatusCodes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.HoldStatusCodes.Where(c => c.Code == id).FirstOrDefaultAsync(); + } + + public void Update(HoldStatus category) + { + _context.HoldStatusCodes.Update(category); + } + + public void Remove(HoldStatus category) + { + _context.HoldStatusCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAccountTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAccountTypeRepository.cs new file mode 100644 index 0000000..c0a2fcb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAccountTypeRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface IAccountTypeRepository + { + Task> ListAllAsync(); + Task AddAsync(AccountType code); + Task FindByIdAsync(int id); + void Update(AccountType code); + void Remove(AccountType code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAllowedStatusChangeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAllowedStatusChangeRepository.cs new file mode 100644 index 0000000..bde6237 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IAllowedStatusChangeRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface IAllowedStatusChangeRepository + { + Task> ListAllAsync(); + Task AddAsync(AllowedStatusChange change); + Task FindByIdAsync(int id); + void Update(AllowedStatusChange change); + void Remove(AllowedStatusChange change); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IBodyStyleRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IBodyStyleRepository.cs new file mode 100644 index 0000000..e5609be --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IBodyStyleRepository.cs @@ -0,0 +1,18 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface IBodyStyleRepository + { + Task> ListAllAsync(); + Task AddAsync(BodyStyle code); + Task FindByIdAsync(string id); + void Update(BodyStyle code); + void Remove(BodyStyle code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICleaningLevelRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICleaningLevelRepository.cs new file mode 100644 index 0000000..946de12 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICleaningLevelRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface ICleaningLevelRepository + { + Task> ListAllAsync(); + Task AddAsync(CleaningLevel code); + Task FindByIdAsync(int id); + void Update(CleaningLevel code); + void Remove(CleaningLevel code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeRepository.cs new file mode 100644 index 0000000..a4109bf --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface ICollateralTypeRepository + { + Task> ListAllAsync(); + Task AddAsync(CollateralType type); + Task FindByIdAsync(int id); + void Update(CollateralType type); + void Remove(CollateralType type); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeToVehicleTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeToVehicleTypeRepository.cs new file mode 100644 index 0000000..6bfc9ee --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ICollateralTypeToVehicleTypeRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface ICollateralTypeToVehicleTypeRepository + { + Task> ListAllAsync(); + Task AddAsync(CollateralTypeToVehicleType type); + Task FindByIdAsync(int id); + void Update(CollateralTypeToVehicleType type); + void Remove(CollateralTypeToVehicleType type); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IHoldStatusRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IHoldStatusRepository.cs new file mode 100644 index 0000000..cfdce6b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IHoldStatusRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface IHoldStatusRepository + { + Task> ListAllAsync(); + Task AddAsync(HoldStatus code); + Task FindByIdAsync(int id); + void Update(HoldStatus code); + void Remove(HoldStatus code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILoanCategoryRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILoanCategoryRepository.cs new file mode 100644 index 0000000..9c8b937 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILoanCategoryRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface ILoanCategoryRepository + { + Task> ListAllAsync(); + Task AddAsync(LoanCategory category); + Task FindByIdAsync(int id); + void Update(LoanCategory category); + void Remove(LoanCategory category); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILocationTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILocationTypeRepository.cs new file mode 100644 index 0000000..8f7c608 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/ILocationTypeRepository.cs @@ -0,0 +1,18 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface ILocationTypeRepository + { + Task> ListAllAsync(); + Task AddAsync(LocationType code); + Task FindByIdAsync(int id); + void Update(LocationType code); + void Remove(LocationType code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IVehicleTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IVehicleTypeRepository.cs new file mode 100644 index 0000000..8f8f2d0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/IVehicleTypeRepository.cs @@ -0,0 +1,15 @@ + + +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public interface IVehicleTypeRepository + { + Task> ListAllAsync(); + Task AddAsync(VehicleType code); + Task FindByIdAsync(string id); + void Update(VehicleType code); + void Remove(VehicleType code); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LoanCategoryRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LoanCategoryRepository.cs new file mode 100644 index 0000000..4f19fff --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LoanCategoryRepository.cs @@ -0,0 +1,44 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class LoanCategoryRepository : BaseRepository, ILoanCategoryRepository + { + + public LoanCategoryRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.LoanCategories.ToListAsync(); + } + + public async Task AddAsync(LoanCategory loanCategories) + { + await _context.LoanCategories.AddAsync(loanCategories); + } + + public async Task FindByIdAsync(int id) + { + return await _context.LoanCategories.Where(c => c.ID == id).FirstOrDefaultAsync(); + } + + public void Update(LoanCategory category) + { + _context.LoanCategories.Update(category); + } + + public void Remove(LoanCategory category) + { + _context.LoanCategories.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LocationTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LocationTypeRepository.cs new file mode 100644 index 0000000..bee206f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/LocationTypeRepository.cs @@ -0,0 +1,44 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class LocationTypeRepository : BaseRepository, ILocationTypeRepository + { + + public LocationTypeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.LocationTypeCodes.ToListAsync(); + } + + public async Task AddAsync(LocationType locationTypeCodes) + { + await _context.LocationTypeCodes.AddAsync(locationTypeCodes); + } + + public async Task FindByIdAsync(int id) + { + return await _context.LocationTypeCodes.Where(c => c.Code == id).FirstOrDefaultAsync(); + } + + public void Update(LocationType category) + { + _context.LocationTypeCodes.Update(category); + } + + public void Remove(LocationType category) + { + _context.LocationTypeCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/VehicleTypeRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/VehicleTypeRepository.cs new file mode 100644 index 0000000..3fc5017 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/Settings/VehicleTypeRepository.cs @@ -0,0 +1,43 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories.Settings +{ + public class VehicleTypeRepository : BaseRepository, IVehicleTypeRepository + { + public VehicleTypeRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.VehicleTypeCodes.ToListAsync(); + } + + public async Task AddAsync(VehicleType vehicleTypeCodes) + { + await _context.VehicleTypeCodes.AddAsync(vehicleTypeCodes); + } + + public async Task FindByIdAsync(string id) + { + return await _context.VehicleTypeCodes.Where(c => c.Code.Equals(id)).FirstOrDefaultAsync(); + } + + public void Update(VehicleType category) + { + _context.VehicleTypeCodes.Update(category); + } + + public void Remove(VehicleType category) + { + _context.VehicleTypeCodes.Remove(category); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/UnitOfWork.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/UnitOfWork.cs new file mode 100644 index 0000000..6c926cd --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/UnitOfWork.cs @@ -0,0 +1,19 @@ +using LCU.API.Internal.RepoApi.Repos.Persistence; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class UnitOfWork : IUnitOfWork + { + private readonly AppDbContext _context; + + public UnitOfWork(AppDbContext context) + { + _context = context; + } + + public async Task CompleteAsync() + { + await _context.SaveChangesAsync(); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/VehicleFloorPricingParametersRepository.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/VehicleFloorPricingParametersRepository.cs new file mode 100644 index 0000000..bab2dec --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Repositories/VehicleFloorPricingParametersRepository.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.RepoApi.Repos.Repositories +{ + public class VehicleFloorPricingParametersRepository : BaseRepository, IVehicleFloorPricingParametersRepository + { + + public VehicleFloorPricingParametersRepository(AppDbContext context) : base(context) + { + } + + public async Task> ListAllAsync() + { + return await _context.VehicleFloorPricingParameters.ToListAsync(); + } + + public async Task AddAsync(VehicleFloorPricingParameters parameters) + { + await _context.VehicleFloorPricingParameters.AddAsync(parameters); + } + + public async Task FindByIdAsync(int id) + { + return await Queryable.Where(_context.VehicleFloorPricingParameters, c => c.Id == id).FirstOrDefaultAsync(); + } + + public void Update(VehicleFloorPricingParameters parameters) + { + _context.VehicleFloorPricingParameters.Update(parameters); + } + + public void Remove(VehicleFloorPricingParameters parameters) + { + _context.VehicleFloorPricingParameters.Remove(parameters); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/ClientHelpers.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/ClientHelpers.cs new file mode 100644 index 0000000..a0ff0c3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/ClientHelpers.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json; +using Serilog; +using System; +using System.Net; +using System.Net.Http; +using System.Text; + +namespace LCU.API.Internal.RepoApi.Repos.Utilities +{ + public class ClientHelpers : IClientHelpers + { + private readonly IHttpClientFactory _clientFactory; + + public ClientHelpers(IHttpClientFactory clientFactory) + { + _clientFactory = clientFactory; + } + + public Uri CreateRequestUri(string clientUrl) + { + return new Uri(clientUrl); + } + + public HttpContent CreateHttpContent(string strPayload) + { + HttpContent content = new StringContent(strPayload, Encoding.UTF8, "application/json"); + return content; + } + + public HttpRequestMessage CreateGetHttpRequestMessage(string clientUrl, string apiKey) + { + var uri = CreateRequestUri(clientUrl); + return CreateHttpRequestMessage(HttpMethod.Get, uri, apiKey); + } + + public HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, Uri uri, string apiKey) + { + HttpRequestMessage request = new HttpRequestMessage() + { + Method = method, + RequestUri = uri, + }; + + request.Headers.Add("api-key", apiKey); + + return request; + } + + public HttpResponseMessage SendHttpRequestMessage(HttpRequestMessage httpRequestMessage) + { + try + { + var client = _clientFactory.CreateClient(); + var response = client.SendAsync(httpRequestMessage).Result; + + return response; + } + catch (Exception ex) + { + Log.Error(ex, "Failure in sending http request message."); + return new HttpResponseMessage(HttpStatusCode.BadRequest); + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/IClientHelpers.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/IClientHelpers.cs new file mode 100644 index 0000000..a8ba7c9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/Utilities/IClientHelpers.cs @@ -0,0 +1,14 @@ +using System; +using System.Net.Http; + +namespace LCU.API.Internal.RepoApi.Repos.Utilities +{ + internal interface IClientHelpers + { + Uri CreateRequestUri(string clientUrl); + HttpContent CreateHttpContent(string payload); + HttpRequestMessage CreateGetHttpRequestMessage(string clientUrl, string apiKey); + HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, Uri uri, string apiKey); + HttpResponseMessage SendHttpRequestMessage(HttpRequestMessage httpRequestMessage); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AuctionAssignment.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AuctionAssignment.cs new file mode 100644 index 0000000..050d47a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AuctionAssignment.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + public class AuctionAssignment + { + [StringLength(4)] public string AuctionID { get; set; } + public string PickupDate { get; set; } + public string LegalSaleDate { get; set; } + public string PickupOrDropOffNote { get; set; } + public int? CleaningLevelRequested { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoIMSWebService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoIMSWebService.cs new file mode 100644 index 0000000..edfe965 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoIMSWebService.cs @@ -0,0 +1,258 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using Serilog; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + public class AutoImsWebService : IAutoImsWebService + { + private string URL = "https://wstls.autoims.com/ClientWebservice?clientId={0}"; + private readonly AutoImsServiceConfig _config; + + public AutoImsWebService(AutoImsServiceConfig config) + { + _config = config; + URL = string.Format(URL, config.ClientId); + } + + public static string GetFormattedVIN(string vin) + { + return vin.Trim().PadLeft(17, '0').ToUpper(); + } + + public async Task RemoveFromAuction(string vin, string clientStockNumber) + { + WebServiceResponse webServiceResponse = null; + + URL = string.Format(URL + "&vin={0}&clientStockNum={1}", vin, clientStockNumber); + using (var client = new HttpClient()) + { + using (var req = new HttpRequestMessage(HttpMethod.Delete, URL)) + { + string authInfo = String.Format("{0}:{1}", _config.Login, _config.Password); + authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo)); + req.Headers.Add("Authorization", "Basic " + authInfo); + req.Headers.Remove("Expect"); + req.Headers.Add("Expect", ""); + + using (HttpResponseMessage resp = await client.SendAsync(req)) + { + //resp.EnsureSuccessStatusCode(); + + var cont = await resp.Content.ReadAsStringAsync(); + webServiceResponse = DeserializeResponse(cont); + } + } + } + + return webServiceResponse; + } + + public async Task PostVehicleAsync(T vehicle) + { + var xml = Serialize(vehicle); + WebServiceResponse WebServiceResponse = null; + + + using (var client = new HttpClient()) + { + using (var content = new StringContent(xml)) + { + content.Headers.Remove("Content-Type"); + content.Headers.Add("Content-Type", "application/xml"); + + using (var req = new HttpRequestMessage(HttpMethod.Post, URL)) + { + string authInfo = String.Format("{0}:{1}", _config.Login, _config.Password); + authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo)); + req.Headers.Add("Authorization", "Basic " + authInfo); + req.Headers.Remove("Expect"); + req.Headers.Add("Expect", ""); + req.Content = content; + + using (HttpResponseMessage resp = await client.SendAsync(req)) + { + var cont = await resp.Content.ReadAsStringAsync(); + WebServiceResponse = DeserializeResponse(cont); + + // 6 is duplicate. + if (WebServiceResponse.Status.Code == 6) + { + Log.Information($"Auto Ims Status Code: { WebServiceResponse.Status.Code}, {WebServiceResponse.Status.Description}"); + WebServiceResponse.Status.Code = 1; + WebServiceResponse.Status.Description = "Notification Already Sent To AutoIMS"; + } + else if (WebServiceResponse.Status.Code != 1) + { + throw new Exception("Auto Ims Status Code: " + WebServiceResponse.Status.Code + " " + string.Join(",", + WebServiceResponse.Status.ValidationError.Select(x => + x.Message))); + } + + //resp.EnsureSuccessStatusCode(); + if (resp.StatusCode != System.Net.HttpStatusCode.OK) + { + throw new Exception("Status Code: " + resp.StatusCode + " " + string.Join(",", + WebServiceResponse.Status.ValidationError.Select(x => + x.Message))); + } + } + } + } + } + + + return WebServiceResponse; + } + + public async Task UpdateAuctionIdentifyingValuesAsync(string customerNum, + string vin, + string auctionAssignmentId) + { + VehicleIdentiferUpdate veh = new VehicleIdentiferUpdate(); + veh.ClientAccountNumber = customerNum; + veh.VIN = vin; + veh.ClientField1 = auctionAssignmentId; + + var response = await PostVehicleAsync(veh); + response.AuctionCollateralVin = vin; + return response; + } + + public async Task AddAuctionNote(string vin, string customerNumber, string note, + string writer) + { + WebServiceResponse response = null; + VehicleNote vehicle = new VehicleNote(); + vehicle.VIN = vin; + + //do not include and AutoIMS will timestamp. + //vehicle.Note.CreationDate = String.Empty;// DateTime.Now.ToString("yyyy-MM-dd"); + vehicle.Note.NoteText = note; + vehicle.Note.Writer = writer; + vehicle.Note.Private = false; + + response = await PostVehicleAsync(vehicle); + response.AuctionCollateralVin = vin; + return response; + } + + public async Task RemoveAuction(string VIN, string customerNumber) + { + WebServiceResponse response = await RemoveFromAuction(VIN, customerNumber); + response.AuctionCollateralVin = VIN; + return response; + } + + public async Task Redeem(string VIN) + { + WebServiceResponse response = null; + RedeemVehicle vehicle = new RedeemVehicle(); + vehicle.VIN = VIN; + vehicle.AuctionAssignment.RedeemVehicle = DateTime.Now.ToString("yyyy-MM-dd"); + + response = await PostVehicleAsync(vehicle); + response.AuctionCollateralVin = VIN; + return response; + } + + public async Task TitleSent(string VIN) + { + WebServiceResponse response = null; + VehicleTitleSent vehicle = new VehicleTitleSent(); + vehicle.VIN = VIN; + vehicle.AuctionAssignment.TitleSent = DateTime.Now.ToString("yyyy-MM-dd"); + + response = await PostVehicleAsync(vehicle); + response.AuctionCollateralVin = VIN; + return response; + } + + public async Task PlaceHold(string VIN, int holdCode, DateTime holdUntil) + { + WebServiceResponse response = null; + VehicleHold vehicle = new VehicleHold(); + vehicle.VIN = VIN; + vehicle.AuctionAssignment.SaleHold.HoldUntil = holdUntil.ToString("yyyy-MM-dd"); + vehicle.AuctionAssignment.SaleHold.HoldStatus = holdCode; + + response = await PostVehicleAsync(vehicle); + response.AuctionCollateralVin = VIN; + return response; + } + + public string GetCollateralImage(string conditionReporUrl) + { + string html = String.Empty; + string imageLink = String.Empty; + + try + { + using (var client = new HttpClient()) + { + var response = client.GetAsync(conditionReporUrl).Result; + + html = response.Content.ReadAsStringAsync().Result; + + var aTag = html.Substring(html.IndexOf("pic_0", StringComparison.OrdinalIgnoreCase)); + + var imgTag = aTag.Substring(aTag.IndexOf("addpic(", StringComparison.OrdinalIgnoreCase) + 7); + + //var src = imgTag.Substring(imgTag.IndexOf("src=\"", StringComparison.InvariantCultureIgnoreCase)); + + imageLink = imgTag.Substring(0, imgTag.IndexOf(",", StringComparison.OrdinalIgnoreCase)) + .Replace("'", "").Replace("\"", ""); + } + } + catch (Exception) + { + imageLink = null; + } + + return imageLink; + } + + private static string Serialize(T vehicle) + { + XmlSerializer xsSubmit = new XmlSerializer(typeof(T)); + + string xml = ""; + + using (var sww = new StringWriter()) + { + using (XmlWriter writer = XmlWriter.Create(sww)) + { + xsSubmit.Serialize(writer, vehicle); + xml = sww.ToString(); + } + } + + return xml; + } + + private T DeserializeResponse(string xml) + { + using (MemoryStream memoryStream = new MemoryStream(StringToByteArrayUtf8(xml))) + { + XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); + + using (StreamReader xmlStreamReader = new StreamReader(memoryStream, Encoding.UTF8)) + { + return (T) xmlSerializer.Deserialize(xmlStreamReader); + } + } + } + + private static byte[] StringToByteArrayUtf8(string value) + { + UTF8Encoding encoding = new UTF8Encoding(); + return encoding.GetBytes(value); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoImsServiceConfig.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoImsServiceConfig.cs new file mode 100644 index 0000000..5385b70 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/AutoImsServiceConfig.cs @@ -0,0 +1,11 @@ + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + public class AutoImsServiceConfig + { + public string URL { get; set; } + public string Login { get; set; } + public string Password { get; set; } + public string ClientId { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/IAutoImsWebService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/IAutoImsWebService.cs new file mode 100644 index 0000000..3c5051a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/IAutoImsWebService.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + public interface IAutoImsWebService + { + Task RemoveFromAuction(string vin, string clientStockNumber); + Task PostVehicleAsync(T vehicle); + + Task UpdateAuctionIdentifyingValuesAsync( string customerNum, + string vin, + string auctionAssignmentId); + + Task AddAuctionNote(string vin, string customerNumber, string note, string writer); + Task RemoveAuction(string VIN, string customerNumber); + Task Redeem(string VIN); + Task TitleSent(string VIN); + Task PlaceHold(string VIN, int holdCode, DateTime holdUntil); + string GetCollateralImage(string conditionReporUrl); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Note.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Note.cs new file mode 100644 index 0000000..78b2a3d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Note.cs @@ -0,0 +1,27 @@ +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class VehicleNote + { + public VehicleNote() + { + Note = new Note(); + } + + public string VIN { get; set; } + public Note Note { get; set; } + } + + public class Note + { + // [MaxLength(25)] + public string Writer { get; set; } + + // public string CreationDate { get; set; } + // [MaxLength(255)] + public string NoteText { get; set; } + public bool Private { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/RedeemVehicle.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/RedeemVehicle.cs new file mode 100644 index 0000000..38b0d3f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/RedeemVehicle.cs @@ -0,0 +1,26 @@ +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class RedeemVehicle + { + public RedeemVehicle() + { + AuctionAssignment = new RedeemAuctionAssignment(); + } + + public string VIN { get; set; } + public RedeemAuctionAssignment AuctionAssignment { get; set; } + } + + [XmlRoot(ElementName = "AuctionAssignment")] + public class RedeemAuctionAssignment + { + /*If set, vehicle is considered 'redeemed' and + will not be sold. Vehicle record will be + archived after designated period of time + (consignor specific)*/ + public string RedeemVehicle { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/UpdateFloorPrice.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/UpdateFloorPrice.cs new file mode 100644 index 0000000..8f6f82d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/UpdateFloorPrice.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class FloorPriceVehicle + { + public FloorPriceVehicle() + { + AuctionAssignment = new FloorPriceAuctionAssignment(); + } + + public string ClientAccountNumber { get; set; } + public string VIN { get; set; } + public FloorPriceAuctionAssignment AuctionAssignment { get; set; } + } + + [XmlRoot(ElementName = "AuctionAssignment")] + public class FloorPriceAuctionAssignment + { + public decimal FloorPrice { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Vehicle.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Vehicle.cs new file mode 100644 index 0000000..c9610b1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/Vehicle.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class Vehicle + { + public Vehicle() + { + AuctionAssignment = new AuctionAssignment(); + VehicleLocation = new VehicleLocation(); + } + + [MaxLength(25)] public string ClientAccountNumber { get; set; } + [StringLength(17)] public string VIN { get; set; } + [MaxLength(30)] public string Make { get; set; } + [MaxLength(30)] public string Model { get; set; } + public int ModelYear { get; set; } + [MaxLength(4)] public string BodyStyle { get; set; } + public int Classification { get; set; } + + + public string ClientField1 { get; set; } + + public bool VehicleIsOperable { get; set; } + + public AuctionAssignment AuctionAssignment { get; set; } + public VehicleLocation VehicleLocation { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleHold.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleHold.cs new file mode 100644 index 0000000..2469395 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleHold.cs @@ -0,0 +1,33 @@ +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class VehicleHold + { + public VehicleHold() + { + AuctionAssignment = new HoldAuctionAssignment(); + } + + public string VIN { get; set; } + public HoldAuctionAssignment AuctionAssignment { get; set; } + } + + [XmlRoot(ElementName = "AuctionAssignment")] + public class HoldAuctionAssignment + { + public HoldAuctionAssignment() + { + SaleHold = new SaleHold(); + } + + public SaleHold SaleHold { get; set; } + } + + public class SaleHold + { + public int HoldStatus { get; set; } + public string HoldUntil { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleIdentiferUpdate.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleIdentiferUpdate.cs new file mode 100644 index 0000000..f0a9944 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleIdentiferUpdate.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class VehicleIdentiferUpdate + { + [MaxLength(25)] public string ClientAccountNumber { get; set; } + [StringLength(17)] public string VIN { get; set; } + public string ClientField1 { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleLocation.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleLocation.cs new file mode 100644 index 0000000..fbe4080 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleLocation.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + public class VehicleLocation + { + public int Type { get; set; } + + [MaxLength(50)] + public string Name { get; set; } + [MaxLength(50)] + public string Address { get; set; } + [MaxLength(50)] + public string City { get; set; } + [MaxLength(20)] + public string State { get; set; } + [MaxLength(20)] + public string Zip { get; set; } + + [MaxLength(30)] + public string Phone { get; set; } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleTitleSent.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleTitleSent.cs new file mode 100644 index 0000000..9e9e291 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/VehicleTitleSent.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + [XmlRoot(ElementName = "Vehicle", Namespace = "http://www.autoims.com/AutoIMSVehicle")] + public class VehicleTitleSent + { + public VehicleTitleSent() + { + AuctionAssignment = new TitleSentAuctionAssignment(); + } + + public string VIN { get; set; } + public TitleSentAuctionAssignment AuctionAssignment { get; set; } + } + + [XmlRoot(ElementName = "AuctionAssignment")] + public class TitleSentAuctionAssignment + { + public string TitleSent { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/WebServiceResponse.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/WebServiceResponse.cs new file mode 100644 index 0000000..bfaa684 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/AutoIMS/WebServiceResponse.cs @@ -0,0 +1,74 @@ +namespace LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS +{ + /// + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] + [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)] + public partial class WebServiceResponse + { + public string AuctionCollateralVin { get; set; } + private WebServiceResponseStatus statusField; + + /// + public WebServiceResponseStatus Status + { + get { return this.statusField; } + set { this.statusField = value; } + } + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] + public partial class WebServiceResponseStatus + { + private int codeField; + + private string descriptionField; + + private WebServiceResponseStatusValidationError[] validationErrorField; + + /// + public int Code + { + get { return this.codeField; } + set { this.codeField = value; } + } + + /// + public string Description + { + get { return this.descriptionField; } + set { this.descriptionField = value; } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("ValidationError")] + public WebServiceResponseStatusValidationError[] ValidationError + { + get { return this.validationErrorField; } + set { this.validationErrorField = value; } + } + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] + public partial class WebServiceResponseStatusValidationError + { + private string codeField; + + private string messageField; + + /// + public string Code + { + get { return this.codeField; } + set { this.codeField = value; } + } + + /// + public string Message + { + get { return this.messageField; } + set { this.messageField = value; } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValuation.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValuation.cs new file mode 100644 index 0000000..faa5025 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValuation.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.NADA +{ + public class CollateralValuation + { + [Required] + public int Id { get; set; } + + public int Year { get; set; } + public string Make { get; set; } + public string Model { get; set; } + public string Uid { get; set; } + public string UcgVehicleId { get; set; } + public DateTime? LookupDate { get; set; } + public string Mileage_Hours { get; set; } + public int? MakeCode { get; set; } + public string MakeDescription { get; set; } + public int? SeriesCode { get; set; } + public string SeriesDescription { get; set; } + public int? BodyCode { get; set; } + public string BodyDescription { get; set; } + public int? Weight { get; set; } + public decimal? Msrp { get; set; } + public int? AveMileage { get; set; } + public decimal? MileageAdjustment { get; set; } + public bool AuctionInfoUnavailable { get; set; } + public int LowAdjustedAuctionValue { get; set; } + public int AverageAdjustedAuctionValue { get; set; } + public int HighAdjustedAuctionValue { get; set; } + public string DateRange { get; set; } + public decimal Loan { get; set; } + public decimal LoanPlusVinAcc { get; set; } + public decimal LoanPlusVinAccMileage { get; set; } + public decimal Retail { get; set; } + public decimal RetailPlusVinAcc { get; set; } + public decimal RetailPlusVinAccMileage { get; set; } + public decimal AvgTradeIn { get; set; } + public decimal AvgTradeInPlusVinAcc { get; set; } + public decimal AvgTradeInPlusVinAccMileage { get; set; } + public decimal RoughTradeIn { get; set; } + public decimal RoughTradeInPlusVinAcc { get; set; } + public decimal RoughTradeInPlusVinAccMileage { get; set; } + public decimal TradeIn { get; set; } + public decimal TradeInPlusVinAcc { get; set; } + public decimal TradeInPlusVinAccMileage { get; set; } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueService.cs new file mode 100644 index 0000000..6e1949e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueService.cs @@ -0,0 +1,127 @@ +using System; +using JdpVehicle; +using Serilog; +using LCU.API.Internal.RepoApi.Repos.Utilities; +using Newtonsoft.Json; +using LCU.API.Internal.RepoApi.Repos.Models.Results; + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.NADA +{ + public class CollateralValueService : ICollateralValueService + { + private readonly CollateralValueServiceConfig _config; + private readonly IClientHelpers _clientHelpers; + + public CollateralValueService(CollateralValueServiceConfig config, ClientHelpers clientHelpers) + { + _config = config; + _clientHelpers = clientHelpers; + } + + + private readonly VehicleTypes[] _nadaVehicleType = + { + VehicleTypes.UsedCar, + VehicleTypes.CommercialTruck + }; + + public CollateralValuation GetCollateralValue(string vin, + int mileage, + int vehicleTypeKey) + { + var valueResponse = this.GetVehicleData(_config.Region, _config.Period, vin, vehicleTypeKey, mileage); + + if (valueResponse != null) + { + var autoVehicle = new CollateralValuation() + { + Make = valueResponse.Make, + Model = valueResponse.Body + " " + valueResponse.Model, + Year = valueResponse.Modelyear, + UcgVehicleId = valueResponse.Ucgvehicleid.ToString(), //ucgvehicleid is the new ID and not 1:1 to legacy UID + SeriesDescription = valueResponse.Model, + BodyDescription = valueResponse.Body, + Weight = String.IsNullOrWhiteSpace(valueResponse.Curbweight) ? 0 : Int32.Parse(valueResponse.Curbweight), + Msrp = String.IsNullOrWhiteSpace(valueResponse.Basemsrp) ? 0 : Int32.Parse(valueResponse.Basemsrp), + AveMileage = valueResponse.Averagemileage, + MileageAdjustment = valueResponse.Mileageadjustment, + LookupDate = DateTime.Now, + Loan = valueResponse.Baseloan, + LoanPlusVinAccMileage = valueResponse.Adjustedloan, + Retail = valueResponse.Basecleanretail, + RetailPlusVinAccMileage = valueResponse.Adjustedcleanretail, + AvgTradeIn = valueResponse.Baseaveragetrade, + AvgTradeInPlusVinAccMileage = valueResponse.Adjustedaveragetrade, + RoughTradeIn = valueResponse.Baseroughtrade, + RoughTradeInPlusVinAccMileage = valueResponse.Adjustedroughtrade, + TradeIn = valueResponse.Basecleantrade, + TradeInPlusVinAccMileage = valueResponse.Adjustedcleantrade, + AuctionInfoUnavailable = true + }; + + //Try catch to prevent failure if no auction values available + try + { + var auctionResponse = this.GetAuctionValues(_config.Period, valueResponse.Ucgvehicleid, _config.Region, vehicleTypeKey, vin, mileage); + + if (auctionResponse != null) + { + autoVehicle.AuctionInfoUnavailable = false; + autoVehicle.AverageAdjustedAuctionValue = auctionResponse.AvgAuction; + autoVehicle.HighAdjustedAuctionValue = auctionResponse.HighAuction; + autoVehicle.LowAdjustedAuctionValue = auctionResponse.LowAuction; + autoVehicle.DateRange = auctionResponse.DateRange; + } + } + catch (Exception ex) + { + Log.Error(ex, "Failed to Retreive Auction Values in CollateralValueService"); + } + + return autoVehicle; + } + + return null; + } + + private VehicleData GetVehicleData(int region, int period, string vin, int vehicleTypeKey, int mileage) + { + var newURL = string.Format(_config.DefaultVehicleAndValuesByVinEndpoint + "?period={0}&vin={1}®ion={2}&mileage={3}", + period, vin, region, mileage); + + var request = _clientHelpers.CreateGetHttpRequestMessage(newURL, _config.ApiKey); + var response = _clientHelpers.SendHttpRequestMessage(request); + + if (!response.IsSuccessStatusCode) + { + throw new Exception($"Request for vehicle data for vehicle with VIN {vin} was unsuccessful."); + } + + var content = response.Content.ReadAsStringAsync().Result; + + var result = JsonConvert.DeserializeObject(content); + + return result.Result[0]; + } + + private AuctionData GetAuctionValues(int period, int ucgVehicleId, int region, int vehicleTypeKey, string vin, int mileage) + { + var newURL = string.Format(_config.WeeklyAuctionValuesByVehicleIdEndPoint + "?period={0}&vehicletype={1}&vin={2}&ucgvehicleid={3}®ion={4}&mileage={5}", + period, this._nadaVehicleType[vehicleTypeKey], vin, ucgVehicleId, region, mileage); + + var request = _clientHelpers.CreateGetHttpRequestMessage(newURL, _config.ApiKey); + var response = _clientHelpers.SendHttpRequestMessage(request); + + if (!response.IsSuccessStatusCode) + { + throw new Exception($"Request for auction data for vehicle with VIN {vin} was unsuccessful."); + } + + var content = response.Content.ReadAsStringAsync().Result; + + var result = JsonConvert.DeserializeObject(content); + + return result.Result[0]; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueServiceConfig.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueServiceConfig.cs new file mode 100644 index 0000000..4cca135 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/CollateralValueServiceConfig.cs @@ -0,0 +1,16 @@ + +namespace LCU.API.Internal.RepoApi.Repos.WebServices.NADA +{ + public class CollateralValueServiceConfig + { + public string Username { get; set; } + public string Password { get; set; } + public string LoginEndpoint { get; set; } + public string VehicleEndpoint { get; set; } + public string DefaultVehicleAndValuesByVinEndpoint { get; set; } + public string WeeklyAuctionValuesByVehicleIdEndPoint { get; set; } + public int Region { get; set; } + public int Period { get; set; } + public string ApiKey { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/ICollateralValueService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/ICollateralValueService.cs new file mode 100644 index 0000000..d6ab9ef --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.Repos/WebServices/NADA/ICollateralValueService.cs @@ -0,0 +1,9 @@ +namespace LCU.API.Internal.RepoApi.Repos.WebServices.NADA +{ + public interface ICollateralValueService + { + CollateralValuation GetCollateralValue(string vin, + int mileage, + int vehicleTypeKey); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.sln b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.sln new file mode 100644 index 0000000..6cff52d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33205.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.RepoApi", "LCU.API.Internal.RepoApi\LCU.API.Internal.RepoApi.csproj", "{CFB340C3-F73D-4552-801D-6B8CFA1FD70A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.RepoApiTests", "LCU.API.Internal.RepoApiTests\LCU.API.Internal.RepoApiTests.csproj", "{5A280599-BF56-4C13-A4B6-2AD17E7BB405}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Library.Internal.RepoApiModels", "LCU.Library.Internal.RepoApiModels\LCU.Library.Internal.RepoApiModels.csproj", "{6B889108-607E-4075-B098-11E9D3497345}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.RepoApi.Repos", "LCU.API.Internal.RepoApi.Repos\LCU.API.Internal.RepoApi.Repos.csproj", "{3167DBBC-9DD9-42B9-BD87-C6CA0788E926}" +EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "LCU.Api.Internal.RepoApiSql", "LCU.Api.Internal.RepoApiSql\LCU.Api.Internal.RepoApiSql.sqlproj", "{C2599576-9529-4AF4-9793-8C532A67EFC3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Release|Any CPU.Build.0 = Release|Any CPU + {5A280599-BF56-4C13-A4B6-2AD17E7BB405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A280599-BF56-4C13-A4B6-2AD17E7BB405}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A280599-BF56-4C13-A4B6-2AD17E7BB405}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A280599-BF56-4C13-A4B6-2AD17E7BB405}.Release|Any CPU.Build.0 = Release|Any CPU + {6B889108-607E-4075-B098-11E9D3497345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B889108-607E-4075-B098-11E9D3497345}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B889108-607E-4075-B098-11E9D3497345}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B889108-607E-4075-B098-11E9D3497345}.Release|Any CPU.Build.0 = Release|Any CPU + {3167DBBC-9DD9-42B9-BD87-C6CA0788E926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3167DBBC-9DD9-42B9-BD87-C6CA0788E926}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3167DBBC-9DD9-42B9-BD87-C6CA0788E926}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3167DBBC-9DD9-42B9-BD87-C6CA0788E926}.Release|Any CPU.Build.0 = Release|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Release|Any CPU.Build.0 = Release|Any CPU + {C2599576-9529-4AF4-9793-8C532A67EFC3}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F5E7D18-CECB-4D1B-87A7-071F1037BDD6} + EndGlobalSection +EndGlobal diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ApiConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ApiConfiguration.cs new file mode 100644 index 0000000..6ba8c02 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ApiConfiguration.cs @@ -0,0 +1,24 @@ +namespace LCU.API.Internal.RepoApi.Configurations; + +public class ApiConfiguration +{ + public string AppName { get; set; } + public string SeqUrl { get; set; } + public string SeqApiKey { get; set; } + public string[] CorsAllowedOrigins { get; set; } + public string RepoManagerAccountUrl { get; set; } + public List ProgrammingRecipients { get; set; } + public List RepoManagerAdminRecipients { get; set; } + public List FloorPriceAcceptedRecipients { get; set; } + public List CollectionsClerkRecipients { get; set; } + public List CreditDebitsRecipients { get; set; } + public List CollectionsRecipients { get; set; } + public string OrganizationCode { get; set; } + public List TransactionTypeCodes { get; set; } + public string TransactionTypeCodeQueryPrefix { get; set; } + public string TransactionHistoryParameterDateFormat { get; set; } + public int TransactionHistoryStartDateOffset { get; set; } + public int TransactionsQueryTimeout { get; set; } + public int SettingsCacheExpirationSeconds { get; set; } + public FeatureFlags FeatureFlags { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CollectionsApiConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CollectionsApiConfiguration.cs new file mode 100644 index 0000000..6356a74 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CollectionsApiConfiguration.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.API.Collections.RepoApi.Configuration.Refactor_Temp; + +public class CollectionsApiConfiguration +{ + public string Url { get; set; } + public int RetryCount { get; set; } + public string ByAccountNo { get; set; } + public string GetConfiguration { get; set; } + public string GetAccountHistory { get; set; } + + [NotMapped] + public string ByAccountNoUrl => Url + ByAccountNo; + [NotMapped] + public string GetConfigurationUrl => Url + GetConfiguration; + [NotMapped] + public string GetAccountHistoryUrl => Url + GetAccountHistory; +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CoreApiWrapperConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CoreApiWrapperConfiguration.cs new file mode 100644 index 0000000..a43dd0e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/CoreApiWrapperConfiguration.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations.Schema; +namespace LCU.API.Collections.RepoApi.Configuration.Refactor_Temp; + +public class CoreApiWrapperConfiguration +{ + public string Url { get; set; } + public int RetryCount { get; set; } + public string TransactionTypeCodeQueryPrefix { get; set; } + public string TransactionHistoryParameterDateFormat { get; set; } + public int TransactionHistoryStartDateOffset { get; set; } + public string GetBranch { get; set; } + public string GetLoanCollateral { get; set; } + public string GetLoanDetails { get; set; } + public string GetTransactionHistory { get; set; } + public string GetMemberByAccountNumber { get; set; } + + [NotMapped] + public string GetBranchUrl => Url + GetBranch; + [NotMapped] + public string GetLoanDetailsUrl => Url + GetLoanDetails; + [NotMapped] + public string GetLoanCollateralUrl => Url + GetLoanCollateral; + [NotMapped] + public string GetTransactionHistoryUrl => Url + GetTransactionHistory; + [NotMapped] + public string GetMemberByAccountNumberUrl => Url + GetMemberByAccountNumber; +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/FeatureFlags.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/FeatureFlags.cs new file mode 100644 index 0000000..df077bd --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/FeatureFlags.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Configurations; + +public class FeatureFlags +{ + public bool RunRepoAccountProcessingConsumer { get; set; } + public bool RunRepoBatchProcessingConsumer { get; set; } + public bool RunRepoBatchCompletionProcessingConsumer { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/NotificationApiConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/NotificationApiConfiguration.cs new file mode 100644 index 0000000..2a0f900 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/NotificationApiConfiguration.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; +namespace LCU.API.Internal.RepoApi.Configurations; + +public class NotificationApiConfiguration +{ + public string Url { get; set; } + public int RetryCount { get; set; } + public string Email { get; set; } + + [NotMapped] + public string EmailUrl => Url + Email; +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ReceiveEndpointConfigurations.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ReceiveEndpointConfigurations.cs new file mode 100644 index 0000000..5544942 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/ReceiveEndpointConfigurations.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.LcuMassTransitModels; +namespace LCU.API.Internal.RepoApi.Configurations; + +public class ReceiveEndpointConfigurations +{ + public ReceiveEndpointConfiguration RepoAccountProcessingReceiveEndpoint { get; set; } + public ReceiveEndpointConfiguration RepoBatchProcessingReceiveEndpoint { get; set; } + public ReceiveEndpointConfiguration RepoBatchCompletionProcessingReceiveEndpoint { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/SqlConnectionConfiguration.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/SqlConnectionConfiguration.cs new file mode 100644 index 0000000..315bc81 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Configurations/SqlConnectionConfiguration.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Configurations; + +public class SqlConnectionConfiguration +{ + public string ConnectionString { get; set; } + public int MaxRetryCount { get; set; } + public TimeSpan MaxRetryDelay { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoAccountProcessingConsumer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoAccountProcessingConsumer.cs new file mode 100644 index 0000000..80b9289 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoAccountProcessingConsumer.cs @@ -0,0 +1,37 @@ +using MassTransit; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.RepoApi.Services.RepoOperationServices; +using System; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Consumers; + +public class RepoAccountProcessingConsumer : IConsumer +{ + private readonly IServiceScopeFactory _scopeFactory; + + public RepoAccountProcessingConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext context) + { + try + { + using var scope = _scopeFactory.CreateScope(); + await Task.Run(() => scope.ServiceProvider.GetRequiredService().HandleRequest(context.Message)); + } + catch (ServiceException ex) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(ex, "Unexpected Exception In Repo API AccountProcessingRequest Consumer"); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(exception, "Unexpected Exception In Repo API AccountProcessingRequest Consumer"); + throw; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchCompletionProcessingConsumer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchCompletionProcessingConsumer.cs new file mode 100644 index 0000000..e3650c7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchCompletionProcessingConsumer.cs @@ -0,0 +1,47 @@ +using MassTransit; +using LCU.Library.Internal.RepoApiModels.Models; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.RepoApi.Services.RepoOperationServices; +using System; +namespace LCU.API.Internal.RepoApi.Consumers; + +public class RepoBatchCompletionProcessingConsumer : IConsumer> +{ + private readonly IServiceScopeFactory _scopeFactory; + + public RepoBatchCompletionProcessingConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext> context) + { + try + { + using var scope = _scopeFactory.CreateScope(); + + var repoBatchCompletionItems = new List(); + + for (int index = 0; index < context.Message.Length; index++) + { + ConsumeContext messageContext = context.Message[index]; + repoBatchCompletionItems.Add(messageContext.Message); + } + + + await Task.Run(() => scope.ServiceProvider.GetRequiredService().HandleRequest(repoBatchCompletionItems)); + } + catch (ServiceException ex) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(ex, "Unexpected Exception In Repo API Batch Completion Consumer"); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(exception, "Unexpected Exception In Repo API Batch Completion Consumer"); + throw; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchProcessingConsumer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchProcessingConsumer.cs new file mode 100644 index 0000000..a8f1c07 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Consumers/RepoBatchProcessingConsumer.cs @@ -0,0 +1,37 @@ +using MassTransit; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.RepoApi.Services.RepoOperationServices; +using System; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Consumers; + +public class RepoBatchProcessingConsumer : IConsumer +{ + private readonly IServiceScopeFactory _scopeFactory; + + public RepoBatchProcessingConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext context) + { + try + { + using var scope = _scopeFactory.CreateScope(); + await Task.Run(() => scope.ServiceProvider.GetRequiredService().HandleRequest(context.Message)); + } + catch (ServiceException ex) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(ex, "Unexpected Exception In Repo API Batch Processing Consumer"); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(exception, "Unexpected Exception In Repo API Batch Processing Consumer"); + throw; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoAccountProcessingController.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoAccountProcessingController.cs new file mode 100644 index 0000000..38d1adc --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoAccountProcessingController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Api.Internal.RepoApi.Services.QueueServices; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RepoAccountProcessingController : ControllerBase +{ + private readonly IQueueRepoAccountProcessingService _queueRepoAccountProcessingService; + + public RepoAccountProcessingController(IQueueRepoAccountProcessingService queueRepoAccountProcessingService) + { + _queueRepoAccountProcessingService = queueRepoAccountProcessingService; + } + + [HttpPost("QueueRepoAccountProcessingRequest")] + [ProducesResponseType(202)] + [ProducesResponseType(500)] + public async Task QueueRepoAccountProcessingRequest([FromBody] List request) + { + try + { + await _queueRepoAccountProcessingService.QueueRepoAccountProcessing(request); + return Accepted(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoBatchProcessingController.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoBatchProcessingController.cs new file mode 100644 index 0000000..30f914b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/RepoBatchProcessingController.cs @@ -0,0 +1,42 @@ +using LCU.Api.Internal.RepoApi.Services.QueueServices; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.RepoApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class RepoBatchProcessingController : ControllerBase + { + private readonly IQueueRepoBatchProcessingService _queueRepoBatchProcessingService; + + public RepoBatchProcessingController(IQueueRepoBatchProcessingService queueRepoBatchProcessingService) + { + _queueRepoBatchProcessingService = queueRepoBatchProcessingService; + } + + + [HttpPost("QueueRepoBatchRequest")] + [ProducesResponseType(202)] + [ProducesResponseType(500)] + public async Task QueueBatchProcessingRequest([FromBody] RepoBatchRequest request) + { + try + { + await _queueRepoBatchProcessingService.QueueRepoBatchInitiate(request); + return Accepted(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/StatusController.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/StatusController.cs new file mode 100644 index 0000000..518c4f6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Controllers/StatusController.cs @@ -0,0 +1,34 @@ +using LCU.Library.Internal.Status.Services; +using Microsoft.AspNetCore.Mvc; +namespace LCU.API.Internal.RepoApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class StatusController : ControllerBase +{ + private readonly IApiHealthCheckService _apiHealthCheckService; + public StatusController(IApiHealthCheckService apiHealthCheckService) + { + _apiHealthCheckService = apiHealthCheckService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult Get() + { + try + { + return Ok(_apiHealthCheckService.CheckApiHealthAndBuildEnvironmentView()); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj new file mode 100644 index 0000000..ab574ec --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi.csproj @@ -0,0 +1,38 @@ + + + net8.0 + enable + true + + + 1701;1702;8618;1591 + False + + + 1701;1702;8618;1591 + False + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AlertProcessingSlip.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AlertProcessingSlip.cs new file mode 100644 index 0000000..da5c719 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AlertProcessingSlip.cs @@ -0,0 +1,23 @@ +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models; + +public class AlertProcessingSlip +{ + public RepoAccountProcessingSlip AccountProcessingSlip { get; set; } + public string Message { get; set; } + public List WatchedStatusTypes { get; set; } + public List WatchedTransactionTypes { get; set; } + + public AlertProcessingSlip + ( + RepoAccountProcessingSlip accountProcessingSlip, + string message, + List watchedStatusTypes, + List watchedTransactionTypes + ) + { + AccountProcessingSlip = accountProcessingSlip; + Message = message; + WatchedStatusTypes = watchedStatusTypes; + WatchedTransactionTypes = watchedTransactionTypes; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AppUser.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AppUser.cs new file mode 100644 index 0000000..535ab60 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/AppUser.cs @@ -0,0 +1,9 @@ +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models; + +public class AppUser +{ + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string[] Roles { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/RepoAccountExtensions.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/RepoAccountExtensions.cs new file mode 100644 index 0000000..4411f02 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/RepoAccountExtensions.cs @@ -0,0 +1,68 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; + +public static class RepoAccountExtensions +{ + public static bool HasAnyCollateral(this RepoAccount repoAccount) + { + return repoAccount?.Collaterals != null && repoAccount.Collaterals.Any(); + } + + public static IReadOnlyCollection GetCollateralVinList(this RepoAccount repoAccount) + { + if (repoAccount?.Collaterals != null && repoAccount.Collaterals.Any()) + { + return repoAccount.Collaterals.Select(x => x.Vin?.Trim().ToUpper()).ToList(); + } + + return new List(); + } + + public static IReadOnlyCollection GetUniqueAuctionAssignmentIds(this RepoAccount repoAccount) + { + var aaList = new List(); + 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/TemenosAccountExtensions.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/TemenosAccountExtensions.cs new file mode 100644 index 0000000..a4ed5c8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Extensions/TemenosAccountExtensions.cs @@ -0,0 +1,81 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/AuctionAssignmentFilter.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/AuctionAssignmentFilter.cs new file mode 100644 index 0000000..2d47ec7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/AuctionAssignmentFilter.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; + +public class AuctionAssignmentFilter : PageFilter +{ + + [FromQuery(Name = "readyForFloorPrice")] + public bool ReadyForFloorPrice { get; set; } + + public AuctionAssignmentFilter() : base() + { + ReadyForFloorPrice = false; + } + + public override object Clone() + { + var jsonString = JsonConvert.SerializeObject(this); + return JsonConvert.DeserializeObject(jsonString, GetType()); + } + +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/PageFilter.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/PageFilter.cs new file mode 100644 index 0000000..626ef4d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Filters/PageFilter.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; + +public class PageFilter : ICloneable +{ + [Range(1, 1000)] + public int Page { get; set; } + + [Range(1, 1000)] + public int Limit { get; set; } + + public PageFilter() + { + Page = 1; + Limit = 100; + } + + public virtual object Clone() + { + var jsonString = JsonConvert.SerializeObject(this); + return JsonConvert.DeserializeObject(jsonString, GetType()); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/RepoAccountProcessingSlip.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/RepoAccountProcessingSlip.cs new file mode 100644 index 0000000..a63f4b1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/RepoAccountProcessingSlip.cs @@ -0,0 +1,95 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +//using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models; + +/** + * 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 TransactionHistory { get; set; } + + public MemberDetail Member { get; set; } + + public LoanDetail Loan { get; set; } + + public IReadOnlyCollection AuctionAssignments { get; set; } + + public IReadOnlyCollection 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."); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/AuctionAssignmentPostRequest.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/AuctionAssignmentPostRequest.cs new file mode 100644 index 0000000..c2d414e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/AuctionAssignmentPostRequest.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS; +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Requests; + +public class AuctionAssignmentPostRequest : IValidatableObject +{ + [Required] + public Shared.Collections.RepoApiLib.Models.AuctionAssignment AuctionAssignment { get; set; } + + [Required] + public IReadOnlyCollection Collaterals { get; set; } + + [Required] + public VehicleLocation VehicleLocation { get; set; } + + [Required] + public int? PrimaryCollateralId { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + var failedValidations = new List(); + + if (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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/RepoAccountPostFilter.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/RepoAccountPostFilter.cs new file mode 100644 index 0000000..ac35245 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Requests/RepoAccountPostFilter.cs @@ -0,0 +1,14 @@ +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Requests; + +public class RepoAccountPostFilter +{ + public List RepoIdList { get; set; } + + public string Status { get; set; } + + public string AuctionHouseId { get; set; } + + public int? RepoCompanyId { get; set; } + + public bool? OnlyOpenAccounts { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/DateTimeResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/DateTimeResult.cs new file mode 100644 index 0000000..5d190db --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/DateTimeResult.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Results; + +public class DateTimeResult : IResult +{ + public DateTime? Value { get; set; } = null; + public string ErrorMessage { get; set; } = null; + public bool IsSuccess { get; set; } = false; +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/IResult.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/IResult.cs new file mode 100644 index 0000000..dcef824 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Models/Old_Repo_Models/Results/IResult.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Results; + +public interface IResult +{ + T Value { get; set; } + bool IsSuccess { get; set; } + string ErrorMessage { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CollectionsDao.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CollectionsDao.cs new file mode 100644 index 0000000..718ea0a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CollectionsDao.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.CodeAnalysis; +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Collections.RepoApi.Configuration.Refactor_Temp; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.Library.Internal.LcuHttpClient.HttpRequest; + +namespace LCU.API.Internal.RepoApi.Old_Repo_DataAccess; + +[ExcludeFromCodeCoverage] +public class CollectionsDao : DaoClient, ICollectionsDao +{ + private readonly ApiConfiguration _apiConfiguration; + private readonly CollectionsApiConfiguration _collectionsApiConfiguration; + private readonly CoreApiWrapperConfiguration _coreApiWrapperConfiguration; + private readonly IGetRequest _getRequest; + + public CollectionsDao(ApiConfiguration apiConfiguration, + CollectionsApiConfiguration collectionsApiConfiguration, + CoreApiWrapperConfiguration coreApiWrapperConfiguration, + IGetRequest getRequest, + IHttpClientFactory clientFactory) : base(clientFactory) + { + _apiConfiguration = apiConfiguration; + _collectionsApiConfiguration = collectionsApiConfiguration; + _coreApiWrapperConfiguration = coreApiWrapperConfiguration; + _getRequest = getRequest; + + } + + public async Task RetrieveAccountAsync(string accountNo) + { + var url = BuildTemenosAccountUrl(accountNo); + var accounts = await _getRequest.AsyncWithNewton>(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + + if (accounts != null && accounts.Any()) + { + return accounts.Where(x => x.AccountIdentifier == accountNo) + .OrderByDescending(x => x.OriginalLoanDate).First(); + } + + return null; + } + + public async Task> RetrieveHistoryAsync(int accountId) + { + var url = _collectionsApiConfiguration.GetAccountHistoryUrl.Replace("{accountId}", accountId.ToString()); + return await _getRequest.AsyncWithNewton>(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + public async Task> RetrieveBranchSetAsync() + { + var url = _coreApiWrapperConfiguration.GetBranchUrl; + return await _getRequest.AsyncWithNewton>(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + public async Task RetrieveTemenosConfigurationAsync() + { + var url = _collectionsApiConfiguration.GetConfigurationUrl; + return await _getRequest.AsyncWithNewton(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + public string BuildTemenosAccountUrl(string accountNo) + { + return _collectionsApiConfiguration.ByAccountNoUrl + _apiConfiguration.OrganizationCode + accountNo; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CoreDao.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CoreDao.cs new file mode 100644 index 0000000..849119f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/CoreDao.cs @@ -0,0 +1,80 @@ +using System.Diagnostics.CodeAnalysis; +using LCU.API.Collections.RepoApi.Configuration.Refactor_Temp; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using LCU.Library.Internal.LcuHttpClient.HttpRequest; + +namespace LCU.API.Internal.RepoApi.Old_Repo_DataAccess; + +[ExcludeFromCodeCoverage] +public class CoreDao : DaoClient, ICoreDao +{ + private readonly ApiConfiguration _apiConfiguration; + private readonly CoreApiWrapperConfiguration _coreApiWrapperConfiguration; + private readonly IGetRequest _getRequest; + + public CoreDao(ApiConfiguration apiConfiguration, + CoreApiWrapperConfiguration coreApiWrapperConfiguration, + IGetRequest getRequest, + IHttpClientFactory clientFactory) : base(clientFactory) + { + _apiConfiguration = apiConfiguration; + _coreApiWrapperConfiguration = coreApiWrapperConfiguration; + _getRequest = getRequest; + } + + public async Task RetrieveMemberAsync(string accountNo) + { + var url = CreateUrlFromAccountNo(_coreApiWrapperConfiguration.GetMemberByAccountNumberUrl, accountNo); + return await _getRequest.AsyncWithNewton(url, (Dictionary)null,0,_apiConfiguration.TransactionsQueryTimeout); + } + + public async Task RetrieveLoanAsync(string accountNo) + { + var url = CreateUrlFromAccountNo(_coreApiWrapperConfiguration.GetLoanDetailsUrl, accountNo); + return await _getRequest.AsyncWithNewton(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + public async Task> RetrieveCollateralAsync(string accountNo) + { + var url = CreateUrlFromAccountNo(_coreApiWrapperConfiguration.GetLoanCollateralUrl, accountNo); + return await _getRequest.AsyncWithNewton>(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + public async Task> RetrieveTransactionsAsync(string accountNo) + { + var url = CreateUrlFromAccountNoAndDateRange(_coreApiWrapperConfiguration.GetTransactionHistoryUrl, accountNo); + return await _getRequest.AsyncWithNewton>(url, (Dictionary)null, 0, _apiConfiguration.TransactionsQueryTimeout); + } + + 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(_apiConfiguration.TransactionHistoryStartDateOffset))); + url = url.Replace("{endDate}", DateTimeForUri(DateTime.Now)); + url += FormatArrayValuesIntoQuery(_apiConfiguration.TransactionTypeCodes, _apiConfiguration.TransactionTypeCodeQueryPrefix); + return url; + } + + private string DateTimeForUri(DateTime dateTime) + { + return dateTime.ToString(_apiConfiguration.TransactionHistoryParameterDateFormat); + } + + private string FormatArrayValuesIntoQuery(List arrayValues, string queryPrefix) + { + var returnString = ""; + foreach (var arrayValue in arrayValues) + { + returnString += queryPrefix + arrayValue; + } + + return returnString; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/DaoClient.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/DaoClient.cs new file mode 100644 index 0000000..10f82ff --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/DaoClient.cs @@ -0,0 +1,80 @@ +using System.Net; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using Newtonsoft.Json; +namespace LCU.API.Internal.RepoApi.Old_Repo_DataAccess; + +public abstract class DaoClient +{ + private readonly IHttpClientFactory _clientFactory; + + //Refactor_TODO: Remove class and should be using our own httpclient library. + protected DaoClient(IHttpClientFactory clientFactory) + { + _clientFactory = clientFactory; + } + + public async Task RetrieveByIdentifierAsync(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( + await response.Content.ReadAsStringAsync()); + + return retrievedObject; + } + catch (Exception ex) + { + Log.Error(ex, $"Error Deserializing {typeof(T).Name} for {identifier}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + if (response.StatusCode == HttpStatusCode.NotFound) + { + return default; + } + + 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 RetrieveAllAsync(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( + await response.Content.ReadAsStringAsync()); + + return retrievedObject; + } + catch (Exception ex) + { + Log.Error(ex, $"Error Deserializing {typeof(T).Name} for {url}"); + throw new Old_Repo_Exceptions.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}"); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICollectionsDao.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICollectionsDao.cs new file mode 100644 index 0000000..2e84f1a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICollectionsDao.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; + +namespace LCU.API.Internal.RepoApi.Old_Repo_DataAccess; + +public interface ICollectionsDao +{ + Task RetrieveAccountAsync(string accountNo); + Task> RetrieveHistoryAsync(int accountId); + Task> RetrieveBranchSetAsync(); + Task RetrieveTemenosConfigurationAsync(); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICoreDao.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICoreDao.cs new file mode 100644 index 0000000..85ff562 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_DataAccess/ICoreDao.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +namespace LCU.API.Internal.RepoApi.Old_Repo_DataAccess; + +public interface ICoreDao +{ + Task RetrieveMemberAsync(string accountNo); + Task RetrieveLoanAsync(string accountNo); + Task> RetrieveCollateralAsync(string accountNo); + Task> RetrieveTransactionsAsync(string accountNo); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionOnHoldChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionOnHoldChecker.cs new file mode 100644 index 0000000..b69c864 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionOnHoldChecker.cs @@ -0,0 +1,34 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class AuctionOnHoldChecker : IAlertChecker +{ + private const string RehoStatus = "REHO"; + + public List CheckForAlert(RepoAccountProcessingSlip slip) + { + var newEventAlerts = new List(); + 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionVinDifferentChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionVinDifferentChecker.cs new file mode 100644 index 0000000..5f24cb3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/AuctionVinDifferentChecker.cs @@ -0,0 +1,45 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class AuctionVinDifferentChecker : IAlertChecker +{ + public List CheckForAlert(RepoAccountProcessingSlip slip) + { + var newEventAlerts = new List(); + 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 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}"; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/CreditAdjustmentChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/CreditAdjustmentChecker.cs new file mode 100644 index 0000000..4b07358 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/CreditAdjustmentChecker.cs @@ -0,0 +1,15 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class CreditAdjustmentChecker : TransactionHistoryChecker +{ + private readonly List _watchedStatuses = new List { "O4R", "REPO", "SOLD" }; + private readonly List _watchedTransactionTypes = new List { "PDSB", "RPMT" }; + private const string Message = "Credit Adjustment Received"; + + public override List CheckForAlert(RepoAccountProcessingSlip slip) + { + return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes)); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DebitAdjustmentChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DebitAdjustmentChecker.cs new file mode 100644 index 0000000..b50b2e4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DebitAdjustmentChecker.cs @@ -0,0 +1,15 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class DebitAdjustmentChecker : TransactionHistoryChecker +{ + private readonly List _watchedStatuses = new List { "O4R", "REPO", "SOLD" }; + private readonly List _watchedTransactionTypes = new List { "CHST", "PRCT" }; + private const string Message = "Debit Adjustment Received"; + + public override List CheckForAlert(RepoAccountProcessingSlip slip) + { + return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes)); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DocumentNotFoundChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DocumentNotFoundChecker.cs new file mode 100644 index 0000000..76e0e07 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/DocumentNotFoundChecker.cs @@ -0,0 +1,59 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_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 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = _document + " Not Found", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/IAlertChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/IAlertChecker.cs new file mode 100644 index 0000000..6de160a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/IAlertChecker.cs @@ -0,0 +1,8 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using EventAlert = LCU.API.Internal.RepoApi.Repos.Models.EventAlert; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public interface IAlertChecker +{ + List CheckForAlert(RepoAccountProcessingSlip slip); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/NoticeOfSurplusDeficiencyNotSentChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/NoticeOfSurplusDeficiencyNotSentChecker.cs new file mode 100644 index 0000000..0d970ac --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/NoticeOfSurplusDeficiencyNotSentChecker.cs @@ -0,0 +1,47 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class NoticeOfSurplusDeficiencyNotSentChecker : IAlertChecker +{ + private const int OwnershipTypeImportant = 6; + private const int DaysBeforeConsideredNotSent = 2; + + public List 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 + { + new EventAlert() + { + RepoId = updatedRepoAccount.RepoId, + AccountNo = updatedRepoAccount.AccountNo, + Message = "Notice of Surplus Deficiency Letter Not Sent", + CreatedBy = "Batch", + DateAdded = DateTime.Now, + EmailRecipient = "RepoManagerAdmin@landmarkcu.com", + EmailBody = CreateEmailBody(updatedRepoAccount) + } + }; + } + + return null; + } + + private string CreateEmailBody(RepoAccount repoAccount) + { + var message = "Notice of Surplus Deficiency Not Sent.

"; + var member = repoAccount.Borrowers?.First(b => b.OwnershipType == OwnershipTypeImportant); + var memberName = "Member Name: " + member?.IndividualName + "
"; + var accountNo = "Account No: " + repoAccount.AccountNo + "
"; + return string.Concat(message, memberName, accountNo); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/O4rNoRepoChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/O4rNoRepoChecker.cs new file mode 100644 index 0000000..b8654d1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/O4rNoRepoChecker.cs @@ -0,0 +1,114 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker +{ + public class O4rNoRepoChecker : IAlertChecker + { + private readonly RepoType _repoType; + private const string O4RStatus = "O4R"; + private const string EmailDestination = "RepoManagerNoRepoCo@landmarkcu.com"; + + 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 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 + { + 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.

"; + var member = repoAccount.Borrowers?.First(b => b.OwnershipType == 6); + var memberName = "Member Name: " + member?.IndividualName + "
"; + var accountNo = "Account No: " + repoAccount.AccountNo + "
"; + var collateral = repoAccount.Collaterals.FirstOrDefault(c => c.RepoCompanySelections?.FirstOrDefault(x => x.RepoType == _repoType) != null); + var year = "Year: " + collateral.Year + "
"; + var make = "Make: " + collateral.Make + "
"; + var model = "Model: " + collateral.Model + "
"; + var vin = "VIN: " + collateral.Vin + "
"; + + return string.Concat(message, memberName, accountNo, year, make, model, vin); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/PaymentReceivedChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/PaymentReceivedChecker.cs new file mode 100644 index 0000000..b8f287c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/PaymentReceivedChecker.cs @@ -0,0 +1,53 @@ +using System.Globalization; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker +{ + public class PaymentReceivedChecker : TransactionHistoryChecker + { + private readonly List _watchedStatuses = new List { "O4R", "REPO", "SOLD" }; + private readonly List _watchedTransactionTypes = new List { "COFR", "POFF", "EXT", "SPMT", "XPMT" }; + private const string EmailDestination = "RepoManagerCreditDebits@landmarkcu.com"; + + public override List CheckForAlert(RepoAccountProcessingSlip slip) + { + var newEventAlerts = new List(); + 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

"; + var member = repoAccount.Borrowers?.First(b => b.OwnershipType == 6); + var memberName = "Member Name: " + member?.IndividualName + "
"; + var accountNo = "Account No: " + repoAccount.AccountNo + "
"; + var paymentAmount = "Payment Amount: " + coreTransaction.amount + "
"; + var nextDueDateString = "Next Due Date: " + nextDueDate + "
"; + var loanBalance = "Loan Balance: " + coreTransaction.runningBalance + "
"; + + return string.Concat(message, memberName, accountNo, paymentAmount, nextDueDateString, loanBalance); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RedemptionEndedNoTitleOrderedChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RedemptionEndedNoTitleOrderedChecker.cs new file mode 100644 index 0000000..be5168e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RedemptionEndedNoTitleOrderedChecker.cs @@ -0,0 +1,31 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class RedemptionEndedNoTitleOrderedChecker : IAlertChecker +{ + private const string RepoStatus = "REPO"; + private const int RedemptionExpiredDaysToWait = 7; + + public List 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = "Redemption Ended No Title Ordered", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotMovedChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotMovedChecker.cs new file mode 100644 index 0000000..7685dd3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotMovedChecker.cs @@ -0,0 +1,64 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker +{ + public class RepoNotMovedChecker : IAlertChecker + { + private readonly int _dayCount; + private const string RepoStatus = "REPO"; + private const int DaysBeforeSendEmail = 14; + private const string EmailDestination = "RepoManagerAdmin@landmarkcu.com"; + + public RepoNotMovedChecker(int dayCount = 30) + { + _dayCount = dayCount; + } + + public List 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 + { + 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; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotSoldChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotSoldChecker.cs new file mode 100644 index 0000000..e5c8f48 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RepoNotSoldChecker.cs @@ -0,0 +1,47 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using EventAlert = LCU.API.Internal.RepoApi.Repos.Models.EventAlert; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class RepoNotSoldChecker : IAlertChecker +{ + private readonly int _dayCount; + private readonly int _repeatDayCount; + private const string RepoStatus = "REPO"; + private const string DestinationEmail = "RepoManagerAdmin@landmarkcu.com"; + + public RepoNotSoldChecker(int dayCount = 30, int repeatDayCount = 0) + { + _dayCount = dayCount; + _repeatDayCount = repeatDayCount; + } + + public List 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 + { + 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RtcLetterNotFoundChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RtcLetterNotFoundChecker.cs new file mode 100644 index 0000000..709fee6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/RtcLetterNotFoundChecker.cs @@ -0,0 +1,66 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class RtcLetterNotFoundChecker : IAlertChecker +{ + private readonly string _document; + private readonly IEnumerable _actionCodes; + private const string O4RStatus = "O4R"; + private const string StateWisconsin = "WI"; + + public RtcLetterNotFoundChecker(string document, IEnumerable actionCodes) + { + _document = document; + _actionCodes = actionCodes.Select(code => code.Trim().ToUpper()); + } + + public List 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 + { + 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 letters) + { + var sentLetterCodes = letters.Select(x => x.ActionCode).ToList(); + foreach (var actionCode in _actionCodes) + { + if (sentLetterCodes.Contains(actionCode)) + { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SaleFundsReceivedChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SaleFundsReceivedChecker.cs new file mode 100644 index 0000000..54b89ed --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SaleFundsReceivedChecker.cs @@ -0,0 +1,15 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class SaleFundsReceivedChecker : TransactionHistoryChecker +{ + private readonly List _watchedStatuses = null; + private readonly List _watchedTransactionTypes = new List { "PRCT" }; + private const string Message = "Sale Funds Received"; + + public override List CheckForAlert(RepoAccountProcessingSlip slip) + { + return StandardizedCheckForAlert(new AlertProcessingSlip(slip, Message, _watchedStatuses, _watchedTransactionTypes)); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipEndChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipEndChecker.cs new file mode 100644 index 0000000..971094d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipEndChecker.cs @@ -0,0 +1,39 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker +{ + public class SkipEndChecker : IAlertChecker + { + private readonly int _dayCount; + private const string O4RStatus = "O4R"; + + public SkipEndChecker(int dayCount = 30) + { + _dayCount = dayCount; + } + + public List CheckForAlert(RepoAccountProcessingSlip slip) + { + if (slip.UpdatedRepoAccount.Status == O4RStatus + && slip.UpdatedRepoAccount.GetDaysInStatus(O4RStatus) >= _dayCount + && slip.UpdatedRepoAccount.IsCurrentlySkipClaim()) + { + return new List + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = "Skip Claim Ended", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + + return null; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipNotPaidChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipNotPaidChecker.cs new file mode 100644 index 0000000..93c742c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipNotPaidChecker.cs @@ -0,0 +1,50 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_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 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = "Skip Not Paid", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipPaidChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipPaidChecker.cs new file mode 100644 index 0000000..f669da6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SkipPaidChecker.cs @@ -0,0 +1,31 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class SkipPaidChecker : IAlertChecker +{ + public const string RepoSubStatusPaidSkip = "PSKP"; + + public List 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = "Skip Claim Paid", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SoldNoProceedsChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SoldNoProceedsChecker.cs new file mode 100644 index 0000000..4995f85 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/SoldNoProceedsChecker.cs @@ -0,0 +1,34 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class SoldNoProceedsChecker : IAlertChecker +{ + private const int DaysToWaitForSold = 5; + + public List 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = "Sold No Proceeds", + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/StatusChangeChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/StatusChangeChecker.cs new file mode 100644 index 0000000..f378dfa --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/StatusChangeChecker.cs @@ -0,0 +1,49 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public class StatusChangeChecker : IAlertChecker +{ + private readonly string _watchStatus; + private readonly IEnumerable _fromStatusList; + private const string NoStatus = ""; + + public StatusChangeChecker(string watchStatus, IEnumerable fromStatusList = null) + { + _watchStatus = watchStatus.Trim().ToUpper(); + _fromStatusList = fromStatusList; + } + + public List 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 + { + 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TimeInStatusChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TimeInStatusChecker.cs new file mode 100644 index 0000000..8e24a23 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TimeInStatusChecker.cs @@ -0,0 +1,45 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using EventAlert = LCU.API.Internal.RepoApi.Repos.Models.EventAlert; +namespace LCU.API.Internal.RepoApi.Old_Repo_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 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 + { + new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + Message = days + " days in " + _checkStatus, + CreatedBy = "Batch", + DateAdded = DateTime.Now + } + }; + } + + return null; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TransactionHistoryChecker.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TransactionHistoryChecker.cs new file mode 100644 index 0000000..7f03e5f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/AlertChecker/TransactionHistoryChecker.cs @@ -0,0 +1,92 @@ +using System.Globalization; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; + +public abstract class TransactionHistoryChecker : IAlertChecker +{ + private readonly int _daysInPastToSearch; + + protected TransactionHistoryChecker(int daysInPastToSearch = 3) + { + _daysInPastToSearch = daysInPastToSearch; + } + + public abstract List CheckForAlert(RepoAccountProcessingSlip slip); + + protected List StandardizedCheckForAlert(AlertProcessingSlip alertProcessingSlip) + { + var newEventAlerts = new List(); + 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 watchedStatusTypes, string status) + { + return watchedStatusTypes?.Contains(status.ToUpper()) ?? true; + } + + public static string FormatTransactionMessage(CoreTransaction transaction) + { + CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); + var message = ""; + if (transaction.transactionTypeCode.ToLower().Contains("undo")) + { + message += "***TRANSACTION UNDONE*** - "; + } + + return message + + $"Transaction Type: \"{transaction.transactionTypeCode}\" | Posting: {transaction.postingDate?.ToString(culture)} | Transaction: {transaction.amount?.ToString("C2", culture)} | Balance: {transaction.runningBalance?.ToString("C2", culture)} | Description: \"{transaction.transactionTypeDescription}\""; + } + + protected List GetRecentTransactionsFromType(List transactionTypes, IReadOnlyCollection transactions) + { + var returnItems = new List(); + + 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 GetRecentTransactionsFromType(string transactionType, int daysInPast, IReadOnlyCollection transactions) + { + return ( + from transaction in transactions + where + transaction.postingDate > DateTime.Now.AddDays(-daysInPast) + && transaction.transactionTypeCode.ToLower().Contains(transactionType.ToLower() + ) + + select transaction + ).ToList(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/IRedemptionExpiredDateCalculator.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/IRedemptionExpiredDateCalculator.cs new file mode 100644 index 0000000..0bb08e7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/IRedemptionExpiredDateCalculator.cs @@ -0,0 +1,9 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain; + +public interface IRedemptionExpiredDateCalculator +{ + DateTime? CalculateAsync(TemenosCase temenosCase, + IReadOnlyCollection readOnlyCollection); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/RedemptionExpiredDateCalculator.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/RedemptionExpiredDateCalculator.cs new file mode 100644 index 0000000..79058b7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Domain/RedemptionExpiredDateCalculator.cs @@ -0,0 +1,36 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Domain; + +public class RedemptionExpiredDateCalculator : IRedemptionExpiredDateCalculator +{ + public DateTime? CalculateAsync(TemenosCase temenosCase, IReadOnlyCollection 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/AccountCreationFailed.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/AccountCreationFailed.cs new file mode 100644 index 0000000..c93cbdf --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/AccountCreationFailed.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.RepoApi.Old_Repo_Exceptions; + +public class AccountCreationFailed : ApplicationException +{ + public AccountCreationFailed(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/IneligibleAccountException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/IneligibleAccountException.cs new file mode 100644 index 0000000..4e61516 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/IneligibleAccountException.cs @@ -0,0 +1,16 @@ +namespace LCU.API.Internal.RepoApi.Old_Repo_Exceptions; + +/// +/// Describes a missing requirement for processing an account; this account CANNOT be tried again later. +/// +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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/MissingRequirementException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/MissingRequirementException.cs new file mode 100644 index 0000000..d120a1b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/MissingRequirementException.cs @@ -0,0 +1,16 @@ +namespace LCU.API.Internal.RepoApi.Old_Repo_Exceptions; + +/// +/// Describes a missing requirement for processing an account; which will mean the account can be tried again later. +/// +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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/NotFoundException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/NotFoundException.cs new file mode 100644 index 0000000..739e92a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/NotFoundException.cs @@ -0,0 +1,12 @@ +namespace LCU.API.Internal.RepoApi.Old_Repo_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) + { + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/ServerErrorException.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/ServerErrorException.cs new file mode 100644 index 0000000..32cde31 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Exceptions/ServerErrorException.cs @@ -0,0 +1,12 @@ +namespace LCU.API.Internal.RepoApi.Old_Repo_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.") { } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/AuctionAssignmentService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/AuctionAssignmentService.cs new file mode 100644 index 0000000..425db0b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/AuctionAssignmentService.cs @@ -0,0 +1,451 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Repos.WebServices.AutoIMS; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; +using AuctionAssignment = LCU.API.Internal.RepoApi.Repos.Models.AuctionAssignment; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using Microsoft.Data.SqlClient; +namespace LCU.API.Internal.RepoApi.Old_Repo_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> 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 Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> GetListByIdsAsync(IReadOnlyCollection idSet) + { + try + { + return await _auctionAssignmentRepository.FindListByIdsAsync(idSet); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving AuctionAssignments for ids: {@idSet}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task 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 Old_Repo_Exceptions.ServerErrorException(); + } + + if (auctionAssignment == null) + { + throw new NotFoundException($" AuctionAssignment with {id} not found."); + } + + return auctionAssignment; + } + + public async Task> 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 Old_Repo_Exceptions.ServerErrorException(); + } + } + + //public async Task 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 Old_Repo_Exceptions.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 Old_Repo_Exceptions.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 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 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 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); + //} + + /** + * 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 RemoveAuctionFromAutoIMS(AuctionAssignment auctionAssignment) + //{ + // return await _autoImsWebService.RemoveFromAuction(auctionAssignment.AuctionCustomerNo, auctionAssignment.CollateralVIN); + //} + + //public IEnumerable CreateJoiningAuctionAssignmentToCollateralsList(AuctionAssignment auctionAssignment, IEnumerable 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(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 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 Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Auction Assignment: {auctionAssignment}", auctionAssignment); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + private async Task> _findActiveAuctionsWithMatchingCollateral(int repoId, IEnumerable 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; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Emailer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Emailer.cs new file mode 100644 index 0000000..69bfb32 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Emailer.cs @@ -0,0 +1,195 @@ +using System.Globalization; +using System.Net; +using System.Text; +using Newtonsoft.Json; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.Library.Internal.NotificationsModels.Models; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.AutoIms; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using LCU.Library.Internal.LcuHttpClient.HttpRequest; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class Emailer : IEmailer +{ + private readonly IPostRequest _postRequest; + private readonly ApiConfiguration _apiConfiguration; + private readonly NotificationApiConfiguration _notificationApiConfiguration; + private readonly IEventAlertService _eventAlertService; + private const string O4RStatus = "O4R"; + + public Emailer(IPostRequest postRequest, + ApiConfiguration apiConfiguration, + NotificationApiConfiguration notificationApiConfiguration, + IEventAlertService eventAlertService) + { + _postRequest = postRequest; + _apiConfiguration = apiConfiguration; + _notificationApiConfiguration = notificationApiConfiguration; + _eventAlertService = eventAlertService; + } + + public async Task SendMessageAsync(List recipient, string subject, string body) + { + var emailRequest = new EmailRequest + { + Recipient = recipient, + Subject = subject, + Body = body + }; + var encoded = JsonConvert.SerializeObject(emailRequest); + + try + { + return await _postRequest.AsyncWithNewton(_notificationApiConfiguration.EmailUrl, emailRequest, _apiConfiguration.TransactionsQueryTimeout); + } + catch (Exception ex) + { + Log.Error(ex, "An exception was caught trying to send an email via Notifications API."); + + return null; + } + } + + public async Task SendFloorPriceAcceptedEmail(bool isManual, string accountNo, decimal? oldFloorPrice, decimal? newFloorPrice) + { + var subject = isManual ? "Manual Floor Price Set" : "Automated Floor Price Set"; + var body = string.Format(subject + " for account {0} from {1} to {2}.", accountNo, oldFloorPrice?.ToString("C2", CultureInfo.CurrentCulture), newFloorPrice?.ToString("C2", CultureInfo.CurrentCulture)); + + return await SendMessageAsync(_apiConfiguration.FloorPriceAcceptedRecipients, subject, body); + } + + public async Task SendSurplusDeficiencyLetterEmailAsync(RepoAccount repoAccount) + { + var message = "Please Send the Surplus Deficiency letter. Please notify the Repo Specialists when the letter has been sent, so that this account can be closed in Repo Manager.

"; + var member = repoAccount.Borrowers?.First(b => b.OwnershipType == 6); + var memberName = "Member Name: " + member?.IndividualName + "
"; + var accountNo = "Account No: " + repoAccount.AccountNo + "
"; + + return await SendMessageAsync(_apiConfiguration.CollectionsClerkRecipients, "Refunds Recieved", string.Concat(message, memberName, accountNo)); + } + + public async Task MaybeSendO4rNoCollateralEmail(RepoAccount repoAccount) + { + if (repoAccount != null && repoAccount.DateClosed == null && repoAccount.Status == O4RStatus) + { + var subject = "O4R No Collateral"; + var body = string.Format("Account {0} set to O4R with no collateral.", repoAccount.AccountNo); + + return await SendMessageAsync(_apiConfiguration.RepoManagerAdminRecipients, subject, body); + } + + return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; + } + + public async Task SendAlertSummaryEmailWithEndTime(DateTime endTime) + { + var startTime = endTime.AddHours(-1); + var eventAlerts = _eventAlertService.ListWithinDateRange(startTime, endTime).GetAwaiter().GetResult(); + var subject = "Repo Manager Alerts " + endTime.ToString("MM-dd-yyyy HH:mm:ss"); + var body = "

" + subject + "


"; + + foreach (var eventAlert in eventAlerts) + { + var entry = CreateAlertEmailEntry(eventAlert); + if (!string.IsNullOrEmpty(entry)) + { + body += CreateAlertEmailEntry(eventAlert) + "

"; + } + } + + if (!eventAlerts.Any()) + { + body += "No new alerts."; + } + + return await SendMessageAsync(_apiConfiguration.CollectionsRecipients, subject, body); + } + + public string CreateAlertEmailEntry(EventAlert eventAlert) + { + var accountNo = "Account No: " + eventAlert.AccountNo + "
"; + var alert = "Alert: " + eventAlert.Message + "
"; + var url = eventAlert.RepoId != null + ? _apiConfiguration.RepoManagerAccountUrl + eventAlert.RepoId + : ""; + var link = eventAlert.RepoId != null + ? string.Format("{0}", url) + "
" + : ""; + + return string.Concat(accountNo, alert, link); + } + + public void SendSummaryEmail(IReadOnlyCollection imported, IReadOnlyCollection floors) + { + Log.Debug("Sending AutoIMS Import Email"); + var subject = "Landmark Auto IMS Import " + DateTime.Now; + var reports = new List(); + + if (imported == null || floors == null) + { + Log.Error("Null result sets passed to SendSummaryEmail"); + return; + } + + foreach (var importResultRow in imported) + { + reports.Add(importResultRow.BuildStatusReport(_apiConfiguration.RepoManagerAccountUrl)); + reports.Add("=====================================================================================================
"); + } + + foreach (var importResultRow in floors) + { + reports.Add(importResultRow.BuildStatusReport(_apiConfiguration.RepoManagerAccountUrl)); + } + + var result = string.Join("
", reports); + SendMessageAsync(_apiConfiguration.CreditDebitsRecipients, subject, result).GetAwaiter().GetResult(); + Log.Debug("AutoIMS Import Email Sent"); + } + + public void SendRefundProcessStartEmails(IReadOnlyCollection models) + { + if (models == null || !models.Any()) + { + Log.Warning("No models to process sold emails for."); + return; + } + var soldResults = models.Where(x => x.Data?.SoldDate?.Date == DateTime.Now.Date).ToList(); + + foreach (var sale in soldResults) + { + SendMessageAsync(_apiConfiguration.CollectionsClerkRecipients, "Auction sold, please start the refund process", CreateRefundProcessEmailBody(sale)).GetAwaiter().GetResult(); + } + } + + public string CreateRefundProcessEmailBody(ImportResultRow sale) + { + if (sale?.RepoAccount == null || sale.Data == null || sale.RepoAccount.Borrowers == null) + { + Log.Error("Invalid data, can't create email body for account."); + return ""; + } + var member = sale.RepoAccount.Borrowers?.First(b => b.OwnershipType == 6); + var individualName = member != null ? member.IndividualName : "No Primary Owner!"; + + var memberName = "Member Name: " + individualName + "
"; + var accountNo = "Account No: " + sale.RepoAccount.AccountNo + "
"; + var collateralVin = "VIN: " + sale.Data.VIN + "
"; + + return string.Concat(memberName, accountNo, collateralVin); + } + + public async Task SendProccessSummary(RepoRunInfo runInfo) + { + + string subject = $"Repo Processor Run Receipt [{runInfo.Receipts.Count}]"; + var receiptList = runInfo.Receipts.Select(receipt => receipt.Pretty()); + string bodyData = string.Join("
", receiptList); + string body = $"Process Started: {runInfo.StartDateTime} and completed at {runInfo.CompleteDateTime}

"; + + return await SendMessageAsync(_apiConfiguration.RepoManagerAdminRecipients, subject, body + bodyData); + + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/EventAlertService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/EventAlertService.cs new file mode 100644 index 0000000..4122087 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/EventAlertService.cs @@ -0,0 +1,290 @@ +using Microsoft.Data.SqlClient; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class EventAlertService : IEventAlertService +{ + private readonly IEventAlertRepository _eventAlertRepository; + private readonly IUnitOfWork _unitOfWork; + private const string RedeemedStatus = "REDEEMED"; + private const string CancelledStatus = "RECA"; + private readonly IRepoAccountRepository _accountRepository; + + public EventAlertService( + IEventAlertRepository eventAlertRepository, + IUnitOfWork unitOfWork, + IRepoAccountRepository accountRepository) + { + _eventAlertRepository = eventAlertRepository; + _unitOfWork = unitOfWork; + _accountRepository = accountRepository; + } + + public async Task> ListAll() + { + try + { + return await _eventAlertRepository.ListAllAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving All Event Alerts"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> ListUnread() + { + try + { + return await _eventAlertRepository.ListUnreadAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving Unread Event Alerts"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> ListWithinDateRange(DateTime start, DateTime end) + { + try + { + return await _eventAlertRepository.ListWithinDateRangeAsync(start, end); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Event Alerts between {start} and {end}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> ListActionNeeded() + { + try + { + var repoNoAuction = await _eventAlertRepository.ListRepoNoAuctionAsync(); + var repoNoCompany = await _eventAlertRepository.ListRepoNoCompanyAsync(); + var o4rNoCompany = await _eventAlertRepository.ListO4rNoCompanyAsync(); + var retitle = await _eventAlertRepository.ListReadyForRetitleAsync(); + var floorUpdateFail = await _eventAlertRepository.ListFloorUpdateFailedAsync(); + var setFloor = await _eventAlertRepository.ListFloorNeedsSettingAsync(); + + return repoNoAuction.Concat(repoNoCompany).Concat(o4rNoCompany).Concat(retitle).Concat(floorUpdateFail).Concat(setFloor).ToList(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving Action Needed Event Alerts"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(EventAlert eventAlert) + { + try + { + if (IsStatusCancelledOrRedeemed(eventAlert.RepoId.Value).Result) + { + throw new UnwantedStatusException(eventAlert.RepoId.Value, eventAlert.AccountNo); + } + await _eventAlertRepository.AddAsync(eventAlert); + await _unitOfWork.CompleteAsync(); + return eventAlert; + } + catch (DuplicateResourceException ex) + { + Log.Debug(ex, $"SaveAsync: Valid Alert Already Exists eventAlert:{eventAlert}", eventAlert); + throw ex; + } + catch (UnwantedStatusException ex) + { + Log.Error(ex, $"SaveAsync: Status of Repo is Redeemed or Cancelled. Event Alert: {eventAlert} not created", eventAlert); + throw; + } + catch (Exception ex) + { + Log.Error(ex, $"SaveAsync: Failure saving Event Alert: {eventAlert}", eventAlert); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> SaveRangeAsync(List eventAlerts) + { + IEnumerable addedEventAlerts = new List(); + try + { + addedEventAlerts = await _eventAlertRepository.AddRangeAsync(eventAlerts); + await _unitOfWork.CompleteAsync(); + } + catch (Exception ex) + { + Log.Error(ex, $"EventAlertService: Failure Saving Range Of Event Alerts: {eventAlerts}", eventAlerts); + } + + return addedEventAlerts; + } + + + + public async Task Get(int id) + { + EventAlert eventAlert; + try + { + eventAlert = await _eventAlertRepository.FindByIdAsync(id); + await _unitOfWork.CompleteAsync(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Event Alert with id: {id}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (eventAlert == null) + { + throw new NotFoundException($"EventAlert with id {id} not found."); + } + + return eventAlert; + } + + public async Task UpdateAsync(EventAlert eventAlert, AppUser user) + { + + EventAlert existingEventAlert = null; + try + { + existingEventAlert = await _eventAlertRepository.FindByIdAsync(eventAlert.Id); + } + catch (Exception ex) + { + Log.Error(ex, $"Failed to find existing event alert with Id: {eventAlert.Id}, user: {user}", eventAlert, user); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (existingEventAlert == null) + { + throw new NotFoundException($"EventAlert with id {eventAlert.Id} not found."); + } + + existingEventAlert.Message = eventAlert.Message; + existingEventAlert.SubMessage = eventAlert.SubMessage; + if (existingEventAlert.DateMarkedRead == null && eventAlert.DateMarkedRead != null) + { + existingEventAlert.MarkedReadBy = user.FirstName + " " + user.LastName; + existingEventAlert.DateMarkedRead = eventAlert.DateMarkedRead; + } + + try + { + _eventAlertRepository.Update(existingEventAlert); + await _unitOfWork.CompleteAsync(); + return existingEventAlert; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Event Alert: {eventAlert}", eventAlert); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Event Alert {eventAlert}", eventAlert); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task DeleteAsync(int id) + { + EventAlert existingEventAlert = null; + try + { + existingEventAlert = await _eventAlertRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + Log.Error(ex, $"Failed to find existing event alert with Id: {id}."); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + if (existingEventAlert == null) + { + throw new NotFoundException($"EventAlert with id {id} not found."); + } + + try + { + _eventAlertRepository.Remove(existingEventAlert); + await _unitOfWork.CompleteAsync(); + return existingEventAlert; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure deleting Event Alert with id {id}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure deleting Event Alert with id {id}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> MarkAllAsRead(string accountNo) + { + IEnumerable existingEventAlerts = null; + try + { + existingEventAlerts = await _eventAlertRepository.ListUnreadByAccountNoAsync(accountNo); + } + catch (Exception ex) + { + Log.Error(ex, $"Failed to find existing event alerts with accountNo: {accountNo}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (existingEventAlerts == null) + { + throw new NotFoundException($"EventAlerts for accountNo: {accountNo} not found."); + } + + var ret = new List(); + + foreach (EventAlert eventAlert in existingEventAlerts) + { + eventAlert.MarkedReadBy = "System"; + eventAlert.DateMarkedRead = DateTime.Now; + + try + { + _eventAlertRepository.Update(eventAlert); + ret.Add(eventAlert); + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Event Alert: {eventAlert} in the list {existingEventAlerts}. Will not commit any changes.", eventAlert); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Event Alert {eventAlert} in the list {existingEventAlerts}. Will not commit any changes", eventAlert); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + await _unitOfWork.CompleteAsync(); + return ret; + } + + private async Task IsStatusCancelledOrRedeemed(int repoId) + { + var repo = await _accountRepository.FindByIdAsync(repoId); + if (repo == null) + { + return false; + } + + return repo.Status.Equals(CancelledStatus) || repo.Status.Equals(RedeemedStatus); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/IRepoQueueConsumerService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/IRepoQueueConsumerService.cs new file mode 100644 index 0000000..d5e49e7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/IRepoQueueConsumerService.cs @@ -0,0 +1,7 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.HostedServices; + +public interface IRepoQueueConsumerService +{ + Task HandleSingleItem(string itemAsString); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/RepoQueueConsumerService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/RepoQueueConsumerService.cs new file mode 100644 index 0000000..824d228 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/HostedServices/RepoQueueConsumerService.cs @@ -0,0 +1,81 @@ +using Newtonsoft.Json; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.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 : IRepoQueueConsumerService +{ + private readonly IServiceScopeFactory _scopeFactory; + + public RepoQueueConsumerService(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task HandleSingleItem(string itemAsString) + { + var result = new QueueResult(); + ProcessingItem item = null; + try + { + using (var scope = _scopeFactory.CreateScope()) + { + + item = JsonConvert.DeserializeObject(itemAsString); + var receipt = new ProcessingReceipt(item.AccountNo) + { + AccountId = item.AccountId ?? "", + CaseId = item.CaseId ?? "", + QueuedBy = item.QueuedBy + }; + + try + { + Log.Information($"Processing {item.AccountNo}"); + var repoAccountProcessor = scope.ServiceProvider.GetRequiredService(); + 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); + } + + result.Receipt = receipt; + return result; + } + } + catch (Exception ex) + { + Log.Fatal(ex, $"[INVESTIGATE] Failed processing {itemAsString}."); + return result; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IAuctionAssignmentService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IAuctionAssignmentService.cs new file mode 100644 index 0000000..5dada35 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IAuctionAssignmentService.cs @@ -0,0 +1,15 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IAuctionAssignmentService +{ + Task> ListByFilterAsync(AuctionAssignmentFilter filter); + Task GetByIdAsync(int id); + Task> GetByRepoIdAsync(int repoId); + //Task SaveAsync(AuctionAssignmentPostRequest auctionAssignmentPostRequest); + //Task UpdateAsync(AuctionAssignment auctionAssignment); + //Task RemoveAuction(int id); + Task> GetListByIdsAsync(IReadOnlyCollection idSet); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEmailer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEmailer.cs new file mode 100644 index 0000000..146ff82 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEmailer.cs @@ -0,0 +1,17 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.AutoIms; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IEmailer +{ + Task SendMessageAsync(List recipient, string subject, string body); + string CreateAlertEmailEntry(EventAlert eventAlert); + Task SendFloorPriceAcceptedEmail(bool isManual, string accountNo, decimal? oldFloorPrice, decimal? newFloorPrice); + Task SendSurplusDeficiencyLetterEmailAsync(RepoAccount repoAccount); + Task MaybeSendO4rNoCollateralEmail(RepoAccount repoAccount); + Task SendAlertSummaryEmailWithEndTime(DateTime endTime); + Task SendProccessSummary(RepoRunInfo runInfo); + void SendSummaryEmail(IReadOnlyCollection imported, IReadOnlyCollection floors); + void SendRefundProcessStartEmails(IReadOnlyCollection imported); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEventAlertService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEventAlertService.cs new file mode 100644 index 0000000..f6b1a4c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IEventAlertService.cs @@ -0,0 +1,17 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IEventAlertService +{ + Task> ListAll(); + Task> ListUnread(); + Task> ListActionNeeded(); + Task> ListWithinDateRange(DateTime start, DateTime end); + Task Get(int id); + Task SaveAsync(EventAlert eventAlert); + Task> SaveRangeAsync(List eventAlerts); + Task UpdateAsync(EventAlert eventAlert, AppUser user); + Task DeleteAsync(int id); + Task> MarkAllAsRead(string eventAlert); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IProcessingReceiptService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IProcessingReceiptService.cs new file mode 100644 index 0000000..650dc3b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IProcessingReceiptService.cs @@ -0,0 +1,15 @@ +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Services +{ + public interface IProcessingReceiptService + { + Task> FindReceiptsAsync(string accountNo, + string accountId = "", string caseId = ""); + + Task> GetReceiptsByAccountNoAsync(string accountNo); + Task> GetRetryListAsync(); + Task SaveAsync(ProcessingReceipt receipt); + Task SaveAllAsync(IReadOnlyCollection receipts); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoAccountService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoAccountService.cs new file mode 100644 index 0000000..466a00d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoAccountService.cs @@ -0,0 +1,23 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Requests; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IRepoAccountService +{ + Task> ListAll(PageFilter pageFilter, RepoAccountPostFilter repoAccountFilter = null); + Task FindByAuctionAssignmentId(int auctionAssignmentId); + Task SaveAsync(RepoAccount repoAccount); + Task Get(int id); + Task> GetByAccountNo(string accountNo); + Task GetNewestOpenAccount(string accountNo); + Task> SearchRepoAccounts(string searchPhrase, int maxSearchResults); + Task UpdateAsync(RepoAccount repoAccount, AppUser currentUser); + Task DeleteAsync(int id); + Task> GetItemsForProcessing(); + RepoAccount SelectFirstOpenAccount(IReadOnlyCollection repoAccounts); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoCompanySelectionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoCompanySelectionService.cs new file mode 100644 index 0000000..f13659c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoCompanySelectionService.cs @@ -0,0 +1,9 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IRepoCompanySelectionService +{ + Task> ListAllAsync(); + Task GetAsync(int id); + Task SaveChangesAsync(IList repoCompanySelections); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoLetterService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoLetterService.cs new file mode 100644 index 0000000..e82330c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/IRepoLetterService.cs @@ -0,0 +1,7 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface IRepoLetterService +{ + Task> ListByAccountIdAsync(int accountId); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ISettingsService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ISettingsService.cs new file mode 100644 index 0000000..a90dfe9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ISettingsService.cs @@ -0,0 +1,31 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public interface ISettingsService +{ + Task GetCachedRepoSettingsAsync(); + Task ListAllSettingsAsync(); + Task SaveAsync(AccountType accountType); + Task SaveAsync(AllowedStatusChange statusChange); + Task SaveAsync(BodyStyle bodyStyle); + Task SaveAsync(CleaningLevel cleaningLevel); + Task SaveAsync(CollateralType collateralType); + Task SaveAsync(CollateralTypeToVehicleType collateralToVehicle); + Task SaveAsync(HoldStatus holdStatus); + Task SaveAsync(LoanCategory loanCategory); + Task SaveAsync(LocationType locationType); + Task SaveAsync(VehicleType vehicleType); + Task UpdateAsync(AccountType accountType); + Task UpdateAsync(AllowedStatusChange statusChange); + Task UpdateAsync(BodyStyle bodyStyle); + Task UpdateAsync(CleaningLevel cleaningLevel); + Task UpdateAsync(CollateralType collateralType); + Task UpdateAsync(CollateralTypeToVehicleType collateralToVehicle); + Task UpdateAsync(HoldStatus holdStatus); + Task UpdateAsync(LoanCategory loanCategory); + Task UpdateAsync(LocationType locationType); + Task UpdateAsync(VehicleType vehicleType); + Task SyncExternalUsersAsync(IReadOnlyCollection users); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ProcessingReceiptService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ProcessingReceiptService.cs new file mode 100644 index 0000000..18b3588 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/ProcessingReceiptService.cs @@ -0,0 +1,88 @@ +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; + +namespace LCU.API.Internal.RepoApi.Old_Repo_Services +{ + public class ProcessingReceiptService : IProcessingReceiptService + { + private readonly IProcessingReceiptRepository _repository; + private readonly IUnitOfWork _unitOfWork; + + public ProcessingReceiptService( + IProcessingReceiptRepository repository, + IUnitOfWork unitOfWork) + { + _repository = repository; + _unitOfWork = unitOfWork; + } + + public async Task> FindReceiptsAsync(string accountNo, + string accountId = "", string caseId = "") + { + try + { + return await _repository.FindReceiptsAsync(accountNo, accountId, caseId); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving receipts"); + throw new ServerErrorException(); + } + } + + public async Task> GetReceiptsByAccountNoAsync(string accountNo) + { + try + { + return await _repository.GetReceiptsByAccountNoAsync(accountNo); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving receipts by accountNo"); + throw new ServerErrorException(); + } + } + + public async Task> GetRetryListAsync() + { + try + { + return await _repository.GetRetryListAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving retry list of receipts"); + throw new ServerErrorException(); + } + } + + public async Task SaveAsync(ProcessingReceipt receipt) + { + try + { + await _repository.AddOrUpdateAsync(receipt); + await _unitOfWork.CompleteAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure saving receipt"); + throw new ServerErrorException(); + } + } + + public async Task SaveAllAsync(IReadOnlyCollection receipts) + { + try + { + await _repository.SaveAllAsync(receipts); + await _unitOfWork.CompleteAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure saving receipts"); + throw new ServerErrorException(); + } + } + + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/AccountAlertCheckService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/AccountAlertCheckService.cs new file mode 100644 index 0000000..6523e35 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/AccountAlertCheckService.cs @@ -0,0 +1,121 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Old_Repo_Domain.AlertChecker; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public class AccountAlertCheckService : IAccountAlertCheckService +{ + private const string O4RStatus = "O4R"; + private const string NoStatus = ""; + private const string RepoStatus = "REPO"; + private const string RecaStatus = "RECA"; + private const string RehoStatus = "REHO"; + private const string RedeemedStatus = "REDEEMED"; + private readonly IEventAlertService _eventAlertService; + + private readonly IEnumerable _accountAlertPreStatusCheckers; + private readonly IEnumerable _accountAlertPrimaryCheckers; + private readonly IEnumerable _accountAlertPrimaryIgnoreWrittenOffCheckers; + + public AccountAlertCheckService(IEventAlertService eventAlertService) + { + _eventAlertService = eventAlertService; + _accountAlertPreStatusCheckers = new List() + { + new SkipPaidChecker() + }; + _accountAlertPrimaryCheckers = new List() + { + new RepoNotMovedChecker( 7), + new RepoNotSoldChecker( 60, 30), + new StatusChangeChecker( RecaStatus), + new StatusChangeChecker( RehoStatus), + new TimeInStatusChecker( RehoStatus, 30, 30), + new StatusChangeChecker( O4RStatus, new List() { NoStatus, RedeemedStatus, RehoStatus, RecaStatus}), + new O4rNoRepoChecker(RepoType.Voluntary), + new O4rNoRepoChecker(RepoType.Involuntary), + new O4rNoRepoChecker(RepoType.PreSkip), + new SkipEndChecker( 91), + new SkipNotPaidChecker( 106, 15), + new DocumentNotFoundChecker("Notice of Plan to Sell", "LTR35", "701", RepoStatus), + new DocumentNotFoundChecker("Notice of Repossession", "LTR34", "701", RepoStatus), + new RtcLetterNotFoundChecker("RTC Letter", new List { "RTC 1", "RTC 3" }), + new SoldNoProceedsChecker(), + new AuctionOnHoldChecker(), + new AuctionVinDifferentChecker(), + new RedemptionEndedNoTitleOrderedChecker() + }; + _accountAlertPrimaryIgnoreWrittenOffCheckers = new List() + { + new DebitAdjustmentChecker(), + new PaymentReceivedChecker(), + new CreditAdjustmentChecker(), + new SaleFundsReceivedChecker() + }; + } + + public Task AlertChecksPreStatusReject(RepoAccountProcessingSlip slip) + { + return CheckForAlerts(_accountAlertPreStatusCheckers, slip); + } + + public async Task ManualAlert(EventAlert alert) + { + return await _eventAlertService.SaveAsync(alert); + } + + public async Task MaybeAlertOnInvalidStatus(RepoAccountProcessingSlip slip) + { + var allowedStatuses = slip.CurrentSettings?.AllowedStatusChanges.Select(x => x.CurrentStatus); + if (slip.UpdatedRepoAccount != null && allowedStatuses != null && + !allowedStatuses.Contains(slip.UpdatedRepoAccount.Status)) + { + await _eventAlertService.SaveAsync(new EventAlert() + { + RepoId = slip.UpdatedRepoAccount.RepoId, + AccountNo = slip.UpdatedRepoAccount.AccountNo, + CreatedBy = "Importer", + DateAdded = DateTime.Now, + Message = $"Collections System Status {slip.UpdatedRepoAccount.Status} Not Valid" + }); + } + } + + public async Task AlertChecksPrimary(RepoAccountProcessingSlip slip) + { + if (slip.TransactionHistory != null && slip.TransactionHistory.Any()) + { + await CheckForAlerts(_accountAlertPrimaryIgnoreWrittenOffCheckers, slip); + } + else + { + Log.Error($"No Transactions Available, skipping Transaction Alerts for {slip.AccountNo}"); + } + + await CheckForAlerts(_accountAlertPrimaryCheckers, slip); + } + + private async Task CheckForAlerts(IEnumerable alertCheckers, RepoAccountProcessingSlip slip) + { + var eventAlerts = new List(); + foreach (var alertChecker in alertCheckers) + { + try + { + var createdAlerts = alertChecker.CheckForAlert(slip); + if (createdAlerts != null) + { + eventAlerts.AddRange(createdAlerts); + } + } + catch (Exception ex) + { + var accountNumber = slip.AccountNo ?? "Account Number Not Found"; + Log.Error(ex, $"Error Checking Alert {alertChecker.GetType()} For Account: {accountNumber}"); + } + } + + await _eventAlertService.SaveRangeAsync(eventAlerts); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IAccountAlertCheckService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IAccountAlertCheckService.cs new file mode 100644 index 0000000..846694c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IAccountAlertCheckService.cs @@ -0,0 +1,20 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public interface IAccountAlertCheckService +{ + /** + * 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? + */ + Task AlertChecksPreStatusReject(RepoAccountProcessingSlip slip); + + /** + * These alerts run after the account has been updated or created and is the primary set of alert checks. + */ + Task AlertChecksPrimary(RepoAccountProcessingSlip slip); + + Task ManualAlert(EventAlert alert); + Task MaybeAlertOnInvalidStatus(RepoAccountProcessingSlip processingSlip); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessor.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessor.cs new file mode 100644 index 0000000..5a3759e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessor.cs @@ -0,0 +1,7 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public interface IRepoAccountProcessor +{ + Task HandleAccount(ProcessingItem item); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessorService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessorService.cs new file mode 100644 index 0000000..4b5ca2e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/IRepoAccountProcessorService.cs @@ -0,0 +1,21 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +//using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public interface IRepoAccountProcessorService +{ + RepoAccount CreateRepoAccount(TemenosAccount account, TemenosCase recoveryCase, LoanDetail detail); + void CheckForRejectedStatusChange(RepoAccount originalRepoAccount, RepoAccount updatedRepoAccount, Settings settings); + RepoAccount UpdateRepoAccountFromParts(RepoAccountProcessingSlip slip); + void UpdateRepoAccountDateClosed(RepoAccount repoAccount); + void TryUpdateCollateralRetitleDate(RepoAccount repo, TemenosCase temenosCase); + void AddAccountUpdateRecord(RepoAccountProcessingSlip slip); + RepoCompanySelection CreateNewRepoCompanyAssignment(Collateral collateral, TemenosCase temenosCase, int? newRepoCompanyId); + void TryUpdateOrCreateRepoCompanyAssignment(RepoAccount repoAccount, + TemenosCase temenosCase, Settings settings); + int? MapRepoAgentIdToRepoCompanyId(string repoAgentId, IReadOnlyCollection repoCompanies); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessor.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessor.cs new file mode 100644 index 0000000..49ab6e1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessor.cs @@ -0,0 +1,324 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Extensions; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public class RepoAccountProcessor : IRepoAccountProcessor +{ + private readonly IRepoAccountProcessorService _repoAccountProcessorService; + private readonly IAccountAlertCheckService _accountAlertCheckService; + private readonly IEmailer _emailer; + private readonly ISettingsService _settingsService; + private readonly ICollectionsDao _collectionsDao; + private readonly ICoreDao _coreDao; + private readonly IRepoAccountService _repoAccountService; + private readonly IRepoLetterService _repoLetterService; + private readonly IAuctionAssignmentService _auctionAssignmentService; + public const string UserStatusFieldOld = "User Status Code 1"; + public const int CaseTypeRepo = 7; // Source: collections API configuration call + private const string O4RStatus = "O4R"; + private const string NoStatus = ""; + private const string RepoStatus = "REPO"; + private const string RecaStatus = "RECA"; + private const string RehoStatus = "REHO"; + private const string RedeemedStatus = "REDEEMED"; + private const string TemenosQueuedBy = "Temenos"; + + public RepoAccountProcessor(IRepoAccountProcessorService repoAccountProcessorService, + IAccountAlertCheckService accountAlertCheckService, + ICollectionsDao collectionsDao, + IEmailer emailer, + ISettingsService settingsService, + IRepoAccountService repoAccountService, + IRepoLetterService repoLetterService, + ICoreDao coreDao, + IAuctionAssignmentService auctionAssignmentService) + { + _repoAccountProcessorService = repoAccountProcessorService; + _accountAlertCheckService = accountAlertCheckService; + _collectionsDao = collectionsDao; + _emailer = emailer; + _settingsService = settingsService; + _repoAccountService = repoAccountService; + _repoLetterService = repoLetterService; + _coreDao = coreDao; + _auctionAssignmentService = auctionAssignmentService; + } + + public async Task HandleAccount(ProcessingItem item) + { + var processingSlip = await CreateProcessingSlip(item); + if (IsAutoCloseAccount(processingSlip)) + { + Log.Debug($"Auto-Closing account {processingSlip.AccountNo} due to no Temenos Account."); + await AutoCloseAccount(processingSlip); + return RepoProcessingResult.Autoclosed; + } + + processingSlip.AssertReadyForPreChecks(); + + // 9640 Attempt to pre-check for a matching status when case is missing. + // If no found case, and the status hasn't changed; instead of failing just skip the unconverted account. + if (IsUnconvertedIntellectAccount(processingSlip)) + { + Log.Debug($"Repo Processor: {item.AccountNo} is not updated for Temenos; skipping."); + return RepoProcessingResult.Unconverted; + } + + processingSlip.RecoveryCase = processingSlip.RecoveryAccount.FindRecoveryCaseOrDefault(processingSlip.CaseId); + + if (!IsPossiblyRepossessionCase(processingSlip)) + { + return RepoProcessingResult.Ineligible; + } + + processingSlip.AssertReadyForFullProcessing(); + + if (ShouldCreateNewAccount(processingSlip)) + { + Log.Debug($"Creating new repo account for {item.AccountNo} : {item.CaseId} "); + processingSlip.UpdatedRepoAccount = await CreateNewRepoAccount(processingSlip); + await _accountAlertCheckService.MaybeAlertOnInvalidStatus(processingSlip); + processingSlip = await AddAlertDataToProcessingSlip(processingSlip); + processingSlip.AssertReadyForAlertChecks(); + await _accountAlertCheckService.AlertChecksPrimary(processingSlip); + return RepoProcessingResult.Created; + } + + if (!processingSlip.ExistingRepoAccount.HasAnyCollateral()) + { + await _emailer.MaybeSendO4rNoCollateralEmail(processingSlip.ExistingRepoAccount); + throw new MissingRequirementException("Existing Account Collateral", + $"{processingSlip.AccountNo} did not have collateral on existing account."); + } + + Log.Debug($"Updating Repo account for {item.AccountNo} : {item.CaseId}"); + processingSlip.UpdatedRepoAccount = await UpdateExistingRepoAccount(processingSlip); + + //Add check for recent account repo closing when in RECA status to prevent reprocessing and new repo account creation with a DateClosed date = null + //The DateClosed date = null will ensure the account gets reprocessed, we don't want alert checks with these accounts + + if (processingSlip.ExistingRepoAccount.DateClosed == null && processingSlip.UpdatedRepoAccount.DateClosed != null) + { + return RepoProcessingResult.Updated; + } + + processingSlip = await AddAlertDataToProcessingSlip(processingSlip); + processingSlip.AssertReadyForAlertChecks(); + await _accountAlertCheckService.AlertChecksPrimary(processingSlip); + return RepoProcessingResult.Updated; + } + + // New method to give a chance to filter out non-repo cases and mark ineligible + private bool IsPossiblyRepossessionCase(RepoAccountProcessingSlip slip) + { + if (slip.RecoveryCase == null) return true; + try + { + if (slip.RecoveryCase.CaseTypeId != CaseTypeRepo) + { + return false; + } + + StatusService.ConvertStatus(slip.RecoveryCase.RepoStatus); + } + catch (IneligibleAccountException) + { + return false; + } + + return true; + } + + private async Task AutoCloseAccount(RepoAccountProcessingSlip processingSlip) + { + AppUser appUser = new AppUser() + { + Email = processingSlip.RequestedBy.QueuedBy, + FirstName = "System", + LastName = "System" + }; + processingSlip.ExistingRepoAccount.DateClosed = DateTime.Now; + processingSlip.ExistingRepoAccount.AccountLastProcessed = DateTime.Now; + await _repoAccountService.UpdateAsync(processingSlip.ExistingRepoAccount, appUser); + } + + private bool IsAutoCloseAccount(RepoAccountProcessingSlip processingSlip) + { + // If recovery account couldn't be fetched, that would have been an exception. + // If recovery account is null, it means "not there", so close the RepoAccount. + // This means close the RepoAccount because the account + // was likely not brought over from Intellect. If something DOES happen, just create + // a new account later.... + return processingSlip.RecoveryAccount == null + && processingSlip.ExistingRepoAccount != null + && processingSlip.ExistingRepoAccount.DateClosed == null; + } + + public async Task CreateProcessingSlip(ProcessingItem item) + { + var accountNo = VerifyAccountNo(item); + + var slip = new RepoAccountProcessingSlip + { + AccountNo = accountNo, + CurrentSettings = await _settingsService.GetCachedRepoSettingsAsync(), + RecoveryAccount = await _collectionsDao.RetrieveAccountAsync(accountNo), + ExistingRepoAccount = await _repoAccountService.GetNewestOpenAccount(accountNo), + Loan = await _coreDao.RetrieveLoanAsync(accountNo), + CaseId = VerifyCaseId(item.CaseId), + RequestedBy = item + }; + + if (slip.RecoveryAccount != null) + { + slip.Letters = await _repoLetterService.ListByAccountIdAsync(slip.RecoveryAccount.AccountId); + } + + return slip; + } + + public async Task AddAlertDataToProcessingSlip(RepoAccountProcessingSlip slip) + { + slip.TransactionHistory = await _coreDao.RetrieveTransactionsAsync(slip.AccountNo); + slip.Member = await _coreDao.RetrieveMemberAsync(slip.AccountNo); + var uniqueIds = slip.UpdatedRepoAccount.GetUniqueAuctionAssignmentIds(); + slip.AuctionAssignments = await _auctionAssignmentService.GetListByIdsAsync(uniqueIds); + return slip; + } + + public bool IsUnconvertedIntellectAccount(RepoAccountProcessingSlip repoAccountSlip) + { + var recoveryAccount = repoAccountSlip.RecoveryAccount; + var caseId = repoAccountSlip.CaseId; + var originalRepoAccount = repoAccountSlip.ExistingRepoAccount; + if (!recoveryAccount.HasRecoveryCase(caseId) && recoveryAccount.HasOldIntellectStatus()) + { + Log.Information( + $"Repo Processor: {repoAccountSlip.AccountNo} has no case, but user status code. Running pre-check."); + return originalRepoAccount.Status == recoveryAccount.CustomFieldMap[UserStatusFieldOld]; + } + + return false; + } + + public int VerifyCaseId(string itemCaseId) + { + try + { + return string.IsNullOrWhiteSpace(itemCaseId) ? 0 : int.Parse(itemCaseId); + } + catch (Exception) + { + throw new IneligibleAccountException( + $"Bad CaseId {itemCaseId}", + $"The CaseId is present, but in a non-integer format."); + } + } + + public string VerifyAccountNo(ProcessingItem item) + { + if (item == null) + { + throw new IneligibleAccountException("Processing Item", "The processing item was null."); + } + + if (item.AccountNo == null) + { + throw new IneligibleAccountException( + "Null AccountNo", + "There should never be null accounts on the queue."); + } + + // if (Regex.IsMatch(item.AccountNo, "^[a-zA-Z0-9]*$")) + // { + // throw new IneligibleAccountException( + // "AccountNo", + // "AccountNo contains alphabetical or symbol characters"); + // } + + return item.AccountNo; + } + + public async Task UpdateExistingRepoAccount(RepoAccountProcessingSlip slip) + { + AppUser appUser = new AppUser() + { + Email = slip.RequestedBy.QueuedBy, + FirstName = "System", + LastName = "System" + }; + + if (ShouldRunCompleteUpdate(slip)) + { + RepoAccount updatedRepoAccount = + _repoAccountProcessorService.UpdateRepoAccountFromParts(slip); + await _accountAlertCheckService.AlertChecksPreStatusReject(slip); + + _repoAccountProcessorService.CheckForRejectedStatusChange(slip.ExistingRepoAccount, updatedRepoAccount, + slip.CurrentSettings); + _repoAccountProcessorService.TryUpdateOrCreateRepoCompanyAssignment(updatedRepoAccount, + slip.RecoveryCase, slip.CurrentSettings); + _repoAccountProcessorService.TryUpdateCollateralRetitleDate(updatedRepoAccount, slip.RecoveryCase); + await _repoAccountService.UpdateAsync(updatedRepoAccount, appUser); + + return updatedRepoAccount; + } + + _repoAccountProcessorService.TryUpdateOrCreateRepoCompanyAssignment(slip.ExistingRepoAccount, + slip.RecoveryCase, slip.CurrentSettings); + _repoAccountProcessorService.AddAccountUpdateRecord(slip); + _repoAccountProcessorService.TryUpdateCollateralRetitleDate(slip.ExistingRepoAccount, slip.RecoveryCase); + _repoAccountProcessorService.UpdateRepoAccountDateClosed(slip.ExistingRepoAccount); + await _repoAccountService.UpdateAsync(slip.ExistingRepoAccount, appUser); + return slip.ExistingRepoAccount; + } + + public bool ShouldRunCompleteUpdate(RepoAccountProcessingSlip slip) + { + if (slip.ExistingRepoAccount == null) + { + throw new MissingRequirementException("Existing Repo Account", + $"Existing Repo Account required for update check, {slip.AccountNo}"); + } + + var newStatus = StatusService.ConvertStatus(slip.RecoveryCase.RepoStatus); + + var dateLastPaid = slip.RecoveryAccount.DateLastPaid != null + ? DateTime.Parse(slip.RecoveryAccount.DateLastPaid) + : slip.ExistingRepoAccount.LastPaymentDate; + var refStatusChanged = slip.RecoveryAccount.AccountStatus != slip.ExistingRepoAccount.ReferenceStatus; + + // This is a special case, it picked up the written off flag, before there was a written off date. + if (string.Equals(slip.ExistingRepoAccount.ReferenceStatus, RepoAccountProcessorService.WrittenOff, + StringComparison.CurrentCultureIgnoreCase) + && slip.ExistingRepoAccount.WrittenOffDate == null) + { + return true; + } + + var needsRedemptionExpired = slip.ExistingRepoAccount.RedemptionExpiredDate == null + && (slip.RecoveryCase.RedemptionExpiredDateIl != null || + slip.RecoveryCase.RedemptionExpiredDateWi != null); + + return !string.Equals(newStatus, slip.ExistingRepoAccount.Status, StringComparison.CurrentCultureIgnoreCase) + || slip.ExistingRepoAccount.LastPaymentDate != dateLastPaid || + slip.ExistingRepoAccount.Balance != slip.RecoveryAccount.Balance + || needsRedemptionExpired || refStatusChanged; + } + + public async Task CreateNewRepoAccount(RepoAccountProcessingSlip slip) + { + var repoAccount = _repoAccountProcessorService.CreateRepoAccount(slip.RecoveryAccount, slip.RecoveryCase, slip.Loan); + var newRepoAccount = await _repoAccountService.SaveAsync(repoAccount); + return newRepoAccount; + } + + public bool ShouldCreateNewAccount(RepoAccountProcessingSlip slip) + { + return slip != null && (slip.ExistingRepoAccount == null || slip.ExistingRepoAccount.DateClosed != null); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessorService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessorService.cs new file mode 100644 index 0000000..e034669 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/Producer/RepoAccountProcessorService.cs @@ -0,0 +1,582 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Results; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Domain; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; + +public class RepoAccountProcessorService : IRepoAccountProcessorService +{ + private const string RedeemedStatus = "REDEEMED"; + private const string PaidSkipStatus = "PSKP"; + private const string RepoCancelledStatus = "RECA"; + public const string WrittenOff = "Written Off"; + public const string SixtyPLusUserAssignedLabel = "SixtyPlus User Assigned"; + private readonly ICoreDao _coreDao; + private readonly IRedemptionExpiredDateCalculator _redemptionExpiredDateCalculator; + private readonly IAccountAlertCheckService _accountAlertCheckService; + private readonly IEventAlertService _eventAlertService; + private readonly IRepoCompanySelectionService _repoCompanySelectionService; + + public RepoAccountProcessorService(ICoreDao coreDao, + IRedemptionExpiredDateCalculator redemptionExpiredDateCalculator, + IAccountAlertCheckService accountAlertCheckService, + IEventAlertService eventAlertService, + IRepoCompanySelectionService repoCompanySelectionService) + { + _coreDao = coreDao; + _redemptionExpiredDateCalculator = redemptionExpiredDateCalculator; + _accountAlertCheckService = accountAlertCheckService; + _eventAlertService = eventAlertService; + _repoCompanySelectionService = repoCompanySelectionService; + } + + // This process determined from the original batch code. (LCU.CollectionRepossessionTracking::IntellecRepoImport) + // -> A) Get XP/Core/Apex Account + // -> B) Get XP/Core/Apex Loan + // -> B.1) Fields needed: Branch, Next due date + // ---> If it can't find a loan, it does nothing... what do we WANT do? + // -> C) Get XP/Core/Apex Loan Collateral + // ---> If there is no collateral, it does nothing... what do we WANT do? + // -> D) Get XP/Core/Apex Borrowers (Individuals) + // ---> No borrowers throws an exception? ... what do we WANT do? + // -> E) Create Repo Account from Loan, Collateral, Borrowers and Temenos Account + public RepoAccount CreateRepoAccount(TemenosAccount account, TemenosCase recoveryCase, LoanDetail loanDetail) + { + var collateralDetailRequest = _coreDao.RetrieveCollateralAsync(account.AccountIdentifier); + + var collateralDetail = collateralDetailRequest.GetAwaiter().GetResult(); + if (collateralDetail == null || !collateralDetail.Any()) + { + throw new AccountCreationFailed( + $"Repo-Account Not Created using Account with AccountNo: {account.AccountIdentifier}. No Collateral."); + } + + var borrowers = ConstructRepoAccountBorrowers(loanDetail); + if (borrowers == null || !borrowers.Any()) + { + throw new AccountCreationFailed( + $"Repo-Account Not Created using Account with AccountNo: {account.AccountIdentifier}. No Borrowers."); + } + + var repoAccount = ConstructRepoAccountFromParts(loanDetail, account, recoveryCase); + repoAccount.Borrowers = borrowers; + repoAccount.Collaterals = ConstructCollateralFromDetails(collateralDetail); + TryUpdateCollateralRetitleDate(repoAccount, recoveryCase); + + Log.Information($"Created Repo-Account for Not-Found Account. AccountNo: {account.AccountIdentifier}"); + return repoAccount; + } + + public List ConstructRepoAccountBorrowers(LoanDetail coreAccount) + { + List borrowers = new List(); + try + { + foreach (var coreAccountBorrower in coreAccount.Borrowers) + { + var borrower = new Borrower() + { + IndividualName = coreAccountBorrower.Name, + IndividualId = coreAccountBorrower.IndividualId.ToString(), + OwnershipType = (int)ConvertBorrowerType(coreAccountBorrower.Type) + }; + borrowers.Add(borrower); + } + } + catch (Exception ex) + { + Log.Error(ex, "Error in ConstructRepoAccountBorrowers"); + } + + return borrowers; + } + + public List ConstructCollateralFromDetails(IReadOnlyCollection collateralDetail) + { + List collateralList = new List(); + foreach (var detail in collateralDetail) + { + var collateral = new Collateral() + { + BodyStyle = null, + CollateralTypeId = null, + Description = detail.Description, + Inoperable = null, + RefCollateralId = detail.CollateralId.ToString() + }; + if (detail.Other != null) + { + collateral.Vin = detail.Other.MiscellaneousInformation; + } + + if (detail.Vehicle != null) + { + collateral.Make = detail.Vehicle.Make; + collateral.Model = detail.Vehicle.Model; + collateral.Vin = detail.Vehicle.VIN; + collateral.Year = detail.Vehicle.Year; + collateral.BodyStyle = detail.Vehicle.BodyStyle; + } + + collateralList.Add(collateral); + } + + return collateralList; + } + + public RepoAccount ConstructRepoAccountFromParts(LoanDetail coreAccount, TemenosAccount account, + TemenosCase recoveryCase) + { + DateTime? lastPaymentDate = null; + DateTime? dateFirstDelinquent = null; + + var lastPaymentResult = GetDateTimeFromString(account.DateLastPaid); + if (lastPaymentResult.IsSuccess) + { + lastPaymentDate = lastPaymentResult.Value; + } + else + { + Log.Warning( + $"Construct Repo Account from temenos accountNo: {account.AccountNumber}. Could not parse last payment result : {account.DateLastPaid}.", + lastPaymentResult, account); + } + + + var dateFirstDelinquentResult = GetDateTimeFromString(account.DateFirstDelinquent); + if (dateFirstDelinquentResult.IsSuccess) + { + dateFirstDelinquent = dateFirstDelinquentResult.Value; + } + else + { + Log.Warning( + $"Construct Repo Account from temenos accountNo: {account.AccountNumber}. failed parsing dateFirstDelinquent {account.DateFirstDelinquent}", + dateFirstDelinquentResult, account); + // This is no longer a "MissingRequirementException", per Bill Johns. + } + + var sixtyPlusUserAssignedNumber = GetSixtyPlusUserNumber(account); + + var repoAccount = new RepoAccount() + { + DueToPayDate = coreAccount.NextDueDate, + Balance = account.Balance, + AccountTypeCode = account.AccountType.Code, + OrigAccountTypeCode = account.AccountType.Code, + LoanNo = long.Parse(account.AccountIdentifier), + MemberNo = MemberIdFromAccountId(account.AccountIdentifier), + Branch = account.Branch != null ? int.Parse(account.Branch) : 0, + AccountNo = account.AccountIdentifier, + Status = StatusService.ConvertStatus(recoveryCase.RepoStatus), + O4RDate = recoveryCase.CreatedDate, + Collector = account.LoanOfficer, + DateFirstDelinquent = dateFirstDelinquent, + DelinquentAmount = account.DQAmount, + CurrentDelinquentAmount = account.DQAmount, + SubStatus = "", + DaysDelinquent = account.DaysDq, + LastPaymentDate = lastPaymentDate, + LstPmtAmt = account.AmountLastPaid, + OrigAmount = account.OriginalLoanAmount, + RequiredPaymentAmount = account.DQAmount, + PrimaryIndividualId = coreAccount.IndividualId[0].ToString(), + Bankruptcy = $"{account.BankruptcyFlag}", + DateFirstIntellect = recoveryCase.CreatedDate, + ReferenceId = account.AccountId, + ReferenceCaseId = recoveryCase.CaseId, + AccountLastProcessed = DateTime.Now, + ReferenceStatus = account.AccountStatus, + WrittenOffDate = coreAccount.Status == WrittenOff ? coreAccount.WrittenOffDate : (DateTime?)null, + SixtyPlusCollectorId = sixtyPlusUserAssignedNumber + }; + + return repoAccount; + } + + public long MemberIdFromAccountId(string accountId) + { + return _coreDao.RetrieveMemberAsync(accountId).GetAwaiter().GetResult().MemberId; + } + + public RepoAccount UpdateRepoAccountFromParts(RepoAccountProcessingSlip slip) + { + RepoAccount originalRepoAccount = slip.ExistingRepoAccount; + TemenosAccount account = slip.RecoveryAccount; + TemenosCase recoveryCase = slip.RecoveryCase; + IReadOnlyCollection letters = slip.Letters; + var redemptionExpiredDate = _redemptionExpiredDateCalculator.CalculateAsync(recoveryCase, letters); + + DateTime? lastPaymentDate = null; + DateTime? dateFirstDelinquent = originalRepoAccount.DateFirstDelinquent; + + var lastPaymentResult = GetDateTimeFromString(account.DateLastPaid); + if (lastPaymentResult.IsSuccess) + { + lastPaymentDate = lastPaymentResult.Value; + } + else + { + Log.Warning( + $"Repo account {originalRepoAccount.RepoId} ({originalRepoAccount.AccountNo}). Could not parse last payment result : {account.DateLastPaid}.", + lastPaymentResult); + } + + var dateFirstDelinquentResult = GetDateTimeFromString(account.DateFirstDelinquent); + if (dateFirstDelinquentResult.IsSuccess) + { + dateFirstDelinquent = dateFirstDelinquentResult.Value; + } + else + { + Log.Error( + $"Repo account {originalRepoAccount.RepoId} ({originalRepoAccount.AccountNo}) failed parsing dateFirstDelinquent {account.DateFirstDelinquent}", + dateFirstDelinquentResult); + // This is no longer a "MissingRequirementException", per Bill Johns. + } + + var sixtyPlusUserAssignedNumber = GetSixtyPlusUserNumber(account); + + var repoAccount = new RepoAccount() + { + RepoId = originalRepoAccount.RepoId, + DueToPayDate = account.DueDate, + Balance = account.Balance, + AccountTypeCode = account.AccountType.Code, + OrigAccountTypeCode = originalRepoAccount.OrigAccountTypeCode, + LoanNo = originalRepoAccount.LoanNo, + MemberNo = originalRepoAccount.MemberNo, + Branch = originalRepoAccount.Branch, + AccountNo = originalRepoAccount.AccountNo, + Status = StatusService.ConvertStatus(recoveryCase.RepoStatus), + O4RDate = originalRepoAccount.O4RDate, + Collector = account.LoanOfficer, + DateFirstDelinquent = dateFirstDelinquent, + DelinquentAmount = account.DQAmount, + CurrentDelinquentAmount = account.DQAmount, + SubStatus = "", + DaysDelinquent = originalRepoAccount.DaysDelinquent, //leave this set to days delinquent when added. + LastPaymentDate = lastPaymentDate, + LstPmtAmt = account.AmountLastPaid ?? 0, + OrigAmount = account.OriginalLoanAmount ?? 0, + RedemptionExpiredDate = redemptionExpiredDate, + RequiredPaymentAmount = account.DQAmount, + PrimaryIndividualId = originalRepoAccount.PrimaryIndividualId, + Bankruptcy = $"{account.BankruptcyFlag}", + Collaterals = originalRepoAccount.Collaterals, + RepoStatusChanges = originalRepoAccount.RepoStatusChanges, + ManualRedemptionDate = originalRepoAccount.ManualRedemptionDate, + DateFirstIntellect = originalRepoAccount.DateFirstIntellect, + Borrowers = originalRepoAccount.Borrowers, + PaidSkipAmount = originalRepoAccount.PaidSkipAmount, + RefundStatus = originalRepoAccount.RefundStatus, + ReferenceId = account.AccountId, + ReferenceCaseId = recoveryCase.CaseId, + AccountLastProcessed = DateTime.Now, + ReferenceStatus = account.AccountStatus, + WrittenOffDate = slip.Loan.Status == WrittenOff ? slip.Loan.WrittenOffDate : (DateTime?)null, + SixtyPlusCollectorId = sixtyPlusUserAssignedNumber + }; + + return repoAccount; + } + + /** + * 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... + */ + public OwnershipType ConvertBorrowerType(string type) + { + if (type != null && string.Equals(type.Trim(), "Borrower", StringComparison.OrdinalIgnoreCase)) + { + return OwnershipType.Borrower; + } + + if (type != null && string.Equals(type.Trim(), "Co-Borrower", StringComparison.OrdinalIgnoreCase)) + { + return OwnershipType.Coborrower; + } + + return OwnershipType.Other; + } + + public void CheckForRejectedStatusChange(RepoAccount originalRepoAccount, RepoAccount updatedRepoAccount, + Repos.Models.Settings.Settings settings) + { + if (!IsStatusMatch(originalRepoAccount, updatedRepoAccount.Status) && + !IsStatusChangeAllowed(originalRepoAccount.Status, updatedRepoAccount.Status, + settings.AllowedStatusChanges)) + { + Log.Warning( + $"Status Change Reverted for Account {updatedRepoAccount.AccountNo} from {originalRepoAccount.Status} to {updatedRepoAccount.Status}"); + + _accountAlertCheckService.ManualAlert(new EventAlert() + { + RepoId = updatedRepoAccount.RepoId, + AccountNo = updatedRepoAccount.AccountNo, + CreatedBy = "Importer", + DateAdded = DateTime.Now, + Message = "Collections System Status Does Not Match Repo Management", + SubMessage = + $"Invalid Status Change from {originalRepoAccount.Status} to {updatedRepoAccount.Status}" + }); + updatedRepoAccount.Status = originalRepoAccount.Status; + } + } + + public bool IsStatusChangeAllowed(string oldStatus, string newStatus, + IEnumerable settingsAllowedStatusChanges) + { + foreach (var allowedStatusChange in settingsAllowedStatusChanges) + { + if (IsStringMatch(allowedStatusChange.CurrentStatus, oldStatus) && + IsStringMatch(allowedStatusChange.AllowedChangeStatus, newStatus)) + { + return true; + } + } + + return false; + } + + public bool IsStatusMatch(RepoAccount repo, string status) + { + if (repo?.Status == null || status == null) + { + return false; + } + + return string.Equals(repo.Status.Trim(), status.Trim(), StringComparison.OrdinalIgnoreCase); + } + + public bool IsStringMatch(string x, string y) + { + if (x == null || y == null) + { + return false; + } + + return string.Equals(x.Trim(), y.Trim(), StringComparison.OrdinalIgnoreCase); + } + + public void UpdateRepoAccountDateClosed(RepoAccount repoAccount) + { + // Close the account after four days in certain statuses + if ((repoAccount.Status == PaidSkipStatus || repoAccount.Status == RedeemedStatus || repoAccount.Status == RepoCancelledStatus) + && repoAccount.RepoStatusChanges.LastOrDefault(x => x.StatusTo == repoAccount.Status)?.ChangedDate < + DateTime.Now.AddDays(-3).Date) + { + repoAccount.DateClosed = DateTime.Now; + repoAccount.AccountLastProcessed = DateTime.Now; + // TODO calling this still has a side effect. + _eventAlertService.MarkAllAsRead(repoAccount.AccountNo).GetAwaiter().GetResult(); + } + } + + public void TryUpdateCollateralRetitleDate(RepoAccount repo, TemenosCase temenosCase) + { + DateTime? retitledDate = (temenosCase.TitleOrdered) ? temenosCase.TitleOrderedDate : null; + + foreach (Collateral collateral in repo.Collaterals) + { + collateral.RetitledInLandmarkName = retitledDate; + } + } + + // This exists to update accounts not originally created from Temenos + public void AddAccountUpdateRecord(RepoAccountProcessingSlip slip) + { + slip.ExistingRepoAccount.ReferenceId = slip.RecoveryAccount.AccountId; + slip.ExistingRepoAccount.ReferenceCaseId = slip.RecoveryCase.CaseId; + slip.ExistingRepoAccount.AccountLastProcessed = DateTime.Now; + } + + public void TryUpdateOrCreateRepoCompanyAssignment(RepoAccount repoAccount, TemenosCase temenosCase, + Repos.Models.Settings.Settings settings) + { + //Temenos may send blank string instead of null + var repoCompanyIdFromCase = string.IsNullOrEmpty(temenosCase.RepoAgentId) + ? null + : MapRepoAgentIdToRepoCompanyId(temenosCase.RepoAgentId, settings.RepoCompanies); + + foreach (var collateral in repoAccount.Collaterals) + { + var mostRecent = collateral.RepoCompanySelections.OrderByDescending(x => x.DateTimeCreated) + .FirstOrDefault(); + + // TODO, move to Slip, save elsewhere. + var updated = + UpdateOrCreateRepoCompanyAssignment(collateral, mostRecent, temenosCase, repoCompanyIdFromCase); + + // This can no longer happen, but watch for it anyway. + if (updated.Any(repoCompanySelection => + repoCompanySelection.DateRepoed != null && + repoCompanySelection.RepoCompanyAssignmentDate == null)) + { + Log.Error( + $"Tried to set DateRepoed before RepoCompanyAssignment for accountNo {repoAccount.AccountNo}. Please Investigate."); + throw new MissingRequirementException( + $"RepoCompanyAssignment for repo account {repoAccount.AccountNo} Missing RepoCompanyAssignmentDate", + $"Cannot assign a DateRepoed before a RepoCompanyAssignment Date."); + } + + if (updated.Any()) + { + _repoCompanySelectionService.SaveChangesAsync(updated).GetAwaiter().GetResult(); + } + } + } + + public List UpdateOrCreateRepoCompanyAssignment(Collateral collateral, RepoCompanySelection mostRecentSelection, + TemenosCase temenosCase, int? repoCompanyIdFromCase) + { + var selectionUpdates = new List(); + //If this is the first repoCompanySelection create when temenos has data + if (mostRecentSelection == null) + { + if (temenosCase.RepoAgentId != null && temenosCase.RepoType != null && + temenosCase.DateRepoAssigned != null) + { + var newRepoCompanyEntry = CreateNewRepoCompanyAssignment(collateral, temenosCase, repoCompanyIdFromCase); + collateral.RepoCompanySelections = collateral.RepoCompanySelections.Append(newRepoCompanyEntry).ToList(); + selectionUpdates.Add(newRepoCompanyEntry); + } + + return selectionUpdates; + } + + //If the repoCompany has changed update with temenos data + if (mostRecentSelection.RepoCompanyId != repoCompanyIdFromCase || + mostRecentSelection.RepoType != temenosCase.RepoType || + mostRecentSelection.RepoCompanyAssignmentDate != temenosCase.DateRepoAssigned) + { + if (mostRecentSelection.DateRepoed == null) + { + mostRecentSelection.DateRepoed = DateTime.Now; + if (mostRecentSelection.RepoCompanyAssignmentDate == null) + { + mostRecentSelection.RepoCompanyAssignmentDate = mostRecentSelection.DateTimeCreated ?? DateTime.Now; + } + + selectionUpdates.Add(mostRecentSelection); + } + + var newRepoCompanyEntry = CreateNewRepoCompanyAssignment(collateral, temenosCase, repoCompanyIdFromCase); + collateral.RepoCompanySelections = collateral.RepoCompanySelections.Append(newRepoCompanyEntry).ToList(); + selectionUpdates.Add(newRepoCompanyEntry); + + return selectionUpdates; + } + + //If no need to update or create, sync Repoed Date + mostRecentSelection.DateRepoed = temenosCase.DateRepossessed; + selectionUpdates.Add(mostRecentSelection); + + return selectionUpdates; + } + + public RepoCompanySelection CreateNewRepoCompanyAssignment(Collateral collateral, TemenosCase temenosCase, int? newRepoCompanyId) + { + // DateRepoAssigned is defaulted to Today. The business (Bill) doesn't want it to throw, if they start having trouble + // with the dates; change this to throw a MissingRequirementException on the field. + var newRepoCompanySelection = new RepoCompanySelection() + { + RepoCompanyId = newRepoCompanyId, + RepoType = temenosCase.RepoType ?? RepoType.Unknown, + RepoCompanyAssignmentDate = temenosCase.DateRepoAssigned ?? DateTime.Now, + DateRepoed = temenosCase.DateRepossessed, + CollateralId = collateral.CollateralId, + DateTimeCreated = DateTime.Now, + Id = null + }; + + return newRepoCompanySelection; + } + + public int? MapRepoAgentIdToRepoCompanyId(string repoAgentId, IReadOnlyCollection repoCompanies) + { + try + { + var repoCompany = + repoCompanies.FirstOrDefault(company => company.ReferenceId == int.Parse(repoAgentId)); + + if (repoCompany != null) + { + return repoCompany.ID; + } + } + catch (Exception ex) + { + Log.Error(ex, $"Error when trying to map RepoAgentID: {repoAgentId} to a RepoCompany ReferenceID", repoCompanies); + } + + // If you get this error, ensure there is a matching RepoCompany and that it has a RepoAgentID that matches the config call in Collections API. + throw new MissingRequirementException("RepoCompanyAssignment - RepoCompanyID", $"Ensure a RepoCompany with RepoAgentID: {repoAgentId} is present"); + } + + public IResult GetDateTimeFromString(string dtString) + { + if (string.IsNullOrEmpty(dtString)) + { + Log.Error("Cannot parse null or empty date string"); + return new DateTimeResult() + { + IsSuccess = false, + ErrorMessage = "Cannot Parse Empty Date String", + }; + } + + try + { + return new DateTimeResult() + { + IsSuccess = true, + Value = DateTime.Parse(dtString) + }; + } + catch (Exception ex) + { + Log.Error(ex, $"Cannot Parse Date String {dtString}"); + return new DateTimeResult() + { + IsSuccess = false, + ErrorMessage = $"There was an Exception when processing the date string: {dtString}" + }; + } + } + + public int? GetSixtyPlusUserNumber(TemenosAccount account) + { + try + { + account.CustomFieldMap.TryGetValue(SixtyPLusUserAssignedLabel, out string sixtyPlusUser); + if (String.IsNullOrWhiteSpace(sixtyPlusUser)) + { + return null; + } + + Int32.TryParse(sixtyPlusUser, out int sixtyPlusUserNumber); + + return sixtyPlusUserNumber; + } + catch(NullReferenceException nullEx) + { + Log.Warning($"60+ Collector key not found in Temenos Account for Account Number: {account.AccountNumber}. Error: {nullEx}"); + return null; + } + catch(Exception ex) + { + Log.Error($"Error while attempting to Parse 60+ Collector ID for Account Number: {account.AccountNumber}. Error: {ex}"); + return null; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoAccountService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoAccountService.cs new file mode 100644 index 0000000..c8cf4fb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoAccountService.cs @@ -0,0 +1,362 @@ +using Microsoft.Data.SqlClient; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Requests; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class RepoAccountService : IRepoAccountService +{ + private readonly IRepoAccountRepository _repoAccountRepository; + private readonly IRepoStatusChangeRepository _repoStatusChangeRepository; + private readonly IRepoManualStatusRepository _repoManualStatusRepository; + private readonly ICollateralRepository _collateralRepository; + private readonly IUnitOfWork _unitOfWork; + + public RepoAccountService( + IRepoAccountRepository repoAccountRepository, + IRepoStatusChangeRepository repoStatusChangeRepository, + IRepoManualStatusRepository repoManualStatusRepository, + ICollateralRepository collateralRepository, + IUnitOfWork unitOfWork) + { + _repoAccountRepository = repoAccountRepository; + _repoStatusChangeRepository = repoStatusChangeRepository; + _repoManualStatusRepository = repoManualStatusRepository; + _collateralRepository = collateralRepository; + _unitOfWork = unitOfWork; + } + + public async Task> ListAll(PageFilter pageFilter, RepoAccountPostFilter repoAccountFilter) + { + try + { + return await _repoAccountRepository.ListAsync( + repoAccountFilter != null ? repoAccountFilter.RepoIdList : new List(), + pageFilter.Page, + pageFilter.Limit, + repoAccountFilter?.Status, + repoAccountFilter?.AuctionHouseId, + repoAccountFilter?.RepoCompanyId, + repoAccountFilter?.OnlyOpenAccounts); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving RepoAccounts"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task FindByAuctionAssignmentId(int auctionAssignmentId) + { + try + { + return await _repoAccountRepository.FindByAuctionAssignmentIdAsync(auctionAssignmentId); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving RepoAccount"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(RepoAccount repoAccount) + { + try + { + ICollection statusChanges = new List(); + + statusChanges.Add(new RepoStatusChange() + { + ChangedBy = "System", + StatusFrom = "None", + StatusTo = repoAccount.Status, + ChangedDate = DateTime.Now + }); + + repoAccount.RepoStatusChanges = statusChanges; + + var newAccount = await _repoAccountRepository.AddAsync(repoAccount); + + await _unitOfWork.CompleteAsync(); + + return repoAccount; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Repo Account: {repoAccount}", repoAccount); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task Get(int id) + { + RepoAccount repoAccount; + try + { + repoAccount = await _repoAccountRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Repo Account with id: {id}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (repoAccount == null) + { + throw new NotFoundException($" Repo Accounts with id {id} not found."); + } + + return repoAccount; + } + + public async Task> GetByAccountNo(string accountNo) + { + IReadOnlyCollection repoAccounts; + try + { + repoAccounts = await _repoAccountRepository.FindByAccountNoAsync(accountNo); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Repo Account by accountNo with accountNo: {accountNo}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (repoAccounts == null || !repoAccounts.Any()) + { + throw new NotFoundException($" Repo Accounts with id {accountNo} not found."); + } + + return repoAccounts; + } + + public async Task UpdateAsync(RepoAccount repoAccount, AppUser currentUser) + { + var existingRepoAccount = await _repoAccountRepository.FindByIdAsync(repoAccount.RepoId); + + if (existingRepoAccount == null) + { + throw new NotFoundException($"Repo Account with repoId {repoAccount.RepoId} not found."); + } + + var originalStatus = existingRepoAccount.Status; + + existingRepoAccount.AccountLastProcessed = repoAccount.AccountLastProcessed; + existingRepoAccount.AccountNo = repoAccount.AccountNo; + existingRepoAccount.AccountTypeCode = repoAccount.AccountTypeCode; + existingRepoAccount.Balance = repoAccount.Balance; + existingRepoAccount.Bankruptcy = repoAccount.Bankruptcy; + existingRepoAccount.Borrowers = repoAccount.Borrowers; + existingRepoAccount.Branch = repoAccount.Branch; + // Only one field can change in collaterals when updating the account. + if (existingRepoAccount.Collaterals != null) + { + foreach (var collateral in existingRepoAccount.Collaterals) + { + var newCollateral = + repoAccount.Collaterals.FirstOrDefault(x => x.CollateralId == collateral.CollateralId); + if (newCollateral != null) + { + collateral.RetitledInLandmarkName = newCollateral.RetitledInLandmarkName; + } + } + } + + // existingRepoAccount.Collaterals = repoAccount.Collaterals; This will fail, save separately. + existingRepoAccount.Collector = repoAccount.Collector; + existingRepoAccount.CurrentDelinquentAmount = repoAccount.CurrentDelinquentAmount; + existingRepoAccount.DateClosed = repoAccount.DateClosed; + existingRepoAccount.DateFirstDelinquent = repoAccount.DateFirstDelinquent; + existingRepoAccount.DateFirstIntellect = repoAccount.DateFirstIntellect; + existingRepoAccount.DaysDelinquent = repoAccount.DaysDelinquent; + existingRepoAccount.DelinquentAmount = repoAccount.DelinquentAmount; + existingRepoAccount.DueToPayDate = repoAccount.DueToPayDate; + existingRepoAccount.LastPaymentDate = repoAccount.LastPaymentDate; + existingRepoAccount.LoanNo = repoAccount.LoanNo; + existingRepoAccount.LstPmtAmt = repoAccount.LstPmtAmt; + existingRepoAccount.ManualRedemptionDate = repoAccount.ManualRedemptionDate; + existingRepoAccount.MemberNo = repoAccount.MemberNo; + existingRepoAccount.O4RDate = repoAccount.O4RDate; + existingRepoAccount.OrigAmount = repoAccount.OrigAmount; + existingRepoAccount.OrigAccountTypeCode = repoAccount.OrigAccountTypeCode; + existingRepoAccount.PaidSkipAmount = repoAccount.PaidSkipAmount; + existingRepoAccount.PrimaryIndividualId = repoAccount.PrimaryIndividualId; + existingRepoAccount.RedemptionExpiredDate = repoAccount.RedemptionExpiredDate; + existingRepoAccount.RefundStatus = repoAccount.RefundStatus; + existingRepoAccount.RequiredPaymentAmount = repoAccount.RequiredPaymentAmount; + existingRepoAccount.ReferenceId = repoAccount.ReferenceId; + existingRepoAccount.ReferenceCaseId = repoAccount.ReferenceCaseId; + existingRepoAccount.ReferenceStatus = repoAccount.ReferenceStatus; + existingRepoAccount.Status = repoAccount.Status; + existingRepoAccount.SubStatus = repoAccount.SubStatus; + existingRepoAccount.WrittenOffDate = repoAccount.WrittenOffDate; + existingRepoAccount.SixtyPlusCollectorId = repoAccount.SixtyPlusCollectorId; + + try + { + _repoAccountRepository.Update(existingRepoAccount); + if (originalStatus != repoAccount.Status) + { + var change = new RepoStatusChange() + { + RepoId = repoAccount.RepoId, + ChangedBy = currentUser.FirstName + " " + currentUser.LastName, + StatusFrom = originalStatus, + StatusTo = repoAccount.Status, + ChangedDate = DateTime.Now + }; + await _repoStatusChangeRepository.AddAsync(change); + } + await _unitOfWork.CompleteAsync(); + return await _repoAccountRepository.FindByIdAsync(repoAccount.RepoId); + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Repo Account: {repoAccount}", repoAccount); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Repo Account: {repoAccount}", repoAccount); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task DeleteAsync(int id) + { + RepoAccount existingRepoAccount; + try + { + existingRepoAccount = await _repoAccountRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + Log.Error(ex, $"Error finding an existing repo to delete with account ID: {id}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (existingRepoAccount == null) + { + throw new NotFoundException($" Repo Account with id {id} not found."); + } + + try + { + _repoAccountRepository.Remove(existingRepoAccount); + await _unitOfWork.CompleteAsync(); + return existingRepoAccount; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure deleting Repo Account with id: {id}", id); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure deleting Repo Account with id: {id}", id); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> GetItemsForProcessing() + { + try + { + var watchStatus = await _repoManualStatusRepository.ListWatchStatusesAsync(); + var repoAccountNos = await _repoAccountRepository.GetItemsForProcessing(watchStatus); + return repoAccountNos; + } + catch (Exception ex) + { + Log.Error(ex, "Failure getting Repo Account Nos for polling"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task> SearchRepoAccounts(string searchPhrase, int maxAllowedResults) + { + try + { + var matchedRepoAccounts = await _repoAccountRepository.SearchRepoAccounts(searchPhrase, maxAllowedResults); + return matchedRepoAccounts; + } + catch (MaxItemException ex) + { + Log.Error(ex, $"Search Phrase Returned More Items Than Max (${maxAllowedResults}) searchPhrase:{searchPhrase}", searchPhrase); + throw new MaxItemException(ex.ItemCount); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure Searching For Repo Accounts searchPhrase: {searchPhrase}", searchPhrase); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task GetNewestOpenAccount(string accountNo) + { + IReadOnlyCollection repoAccounts; + try + { + repoAccounts = await _repoAccountRepository.FindByAccountNoAsync(accountNo); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Repo Account by accountNo with accountNo: {accountNo}"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + + if (repoAccounts == null || !repoAccounts.Any()) + { + // You can't throw here, it's not an exception case, there simply isn't an account. (404) + Log.Debug($" Repo Accounts with id {accountNo} not found."); + return null; + } + + var account = SelectFirstOpenAccount(repoAccounts); + + //Adding Collateral. Uncertain how this is handled in the current implementatoin, but it is still checked for? So, hhrm? + //account.Collaterals = await _collateralRepository.FindByRepoIdAsync(account.RepoId) as ICollection; + + return account; + } + + /** + * Attempts to select the repo account that should be updated. Since we have to go by AccountNo and there + * may be more than one. Try to return an open account, and log if it detects more than one. + */ + public RepoAccount SelectFirstOpenAccount(IReadOnlyCollection repoAccounts) + { + if (repoAccounts.Count == 1) + { + return repoAccounts.First(); + } + + RepoAccount openAccount = null; + foreach (var repoAccount in repoAccounts.OrderBy(x => x.RepoId)) + { + if (repoAccount.DateClosed == null) + { + if (openAccount != null) + { + Log.Fatal($"Multiple -OPEN- Repos with AccountNo: {openAccount.AccountNo}, {openAccount.RepoId} / {repoAccount.RepoId}"); + return openAccount; + } + openAccount = repoAccount; + } + } + + if (openAccount != null) + { + return openAccount; + } + + return repoAccounts.OrderByDescending(x => x.DateClosed).ToList().FirstOrDefault(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoCompanySelectionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoCompanySelectionService.cs new file mode 100644 index 0000000..031e439 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoCompanySelectionService.cs @@ -0,0 +1,59 @@ +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class RepoCompanySelectionService : IRepoCompanySelectionService +{ + private readonly IRepoCompanySelectionRepository _repoCompanySelectionRepository; + private readonly IUnitOfWork _unitOfWork; + + public RepoCompanySelectionService( + IRepoCompanySelectionRepository repoCompanySelectionRepository, + IUnitOfWork unitOfWork) + { + _repoCompanySelectionRepository = repoCompanySelectionRepository; + _unitOfWork = unitOfWork; + } + + public async Task> ListAllAsync() + { + try + { + return await _repoCompanySelectionRepository.ListAllAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving Repo Companies"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task GetAsync(int id) + { + RepoCompanySelection repoCompanySelection; + try + { + repoCompanySelection = await _repoCompanySelectionRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure retrieving Repo Company Selection with id: {id}"); + throw new NotFoundException($"Repo Company Selection with id {id} not found"); + } + + if (repoCompanySelection == null) + { + throw new NotFoundException($"Repo Company Selection with id {id} not found"); + } + + return repoCompanySelection; + } + + public async Task SaveChangesAsync(IList repoCompanySelections) + { + _repoCompanySelectionRepository.SaveRange(repoCompanySelections); + await _unitOfWork.CompleteAsync(); + } + +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoLetterService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoLetterService.cs new file mode 100644 index 0000000..ff63a38 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/RepoLetterService.cs @@ -0,0 +1,85 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class RepoLetterService : IRepoLetterService +{ + private readonly ILetterRepository _letterRepository; + private readonly ICollectionsDao _collectionsDao; + + public RepoLetterService(ILetterRepository letterRepository, + ICollectionsDao collectionsDao) + { + _letterRepository = letterRepository; + _collectionsDao = collectionsDao; + } + + public async Task> ListByAccountIdAsync(int accountId) + { + try + { + var collectionsHistory = await _collectionsDao.RetrieveHistoryAsync(accountId); + var collectionsLetters = + collectionsHistory.Where(history => history.Type == HistoryType.Letter).ToList(); + + if (!collectionsLetters.Any()) + { + return new List(); + } + + var letters = await _letterRepository.ListAsync(); + return ProcessLetters(collectionsLetters, letters.ToList()); + } + catch (MissingRequirementException ex) + { + throw; + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving Letter List"); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public static IReadOnlyCollection ProcessLetters(List collectionsLetters, + List letters) + { + // GJF: Are you here to fix a bug? Probably. These don't match up, who wrote this?!?! This code is fueled by hope. + // Ok, so you really need to fix it. Go out to Temenos, go to System Management (the button is labelled 'tools'!) + // Find the Communication section, expand it and choose 'letters & forms'. Now find the right Letter and + // update the LetterDescription column in the Letters table of RepoDb for the correct letter. + var resultList = new List(); + foreach (var letter in letters) + { + foreach (var collectionsLetter in collectionsLetters) + { + if (string.IsNullOrWhiteSpace(letter.LetterDescription)) + { + // If this exception is happening, see LetterUpdate.sql. Also See Above. + throw new MissingRequirementException("LetterDescription", "Letter table requires LetterDescription Column entry."); + } + + if (collectionsLetter.Message.Trim().ToUpper() + .Contains(letter.LetterDescription.Trim().ToUpper())) + { + resultList.Add(new RepoLetter + { + NoticeType = collectionsLetter.Message, + DateSent = collectionsLetter.Created, + DateExpired = collectionsLetter.Created.AddDays(letter.DaysToWait ?? 0), + DaysToRedeem = letter.DaysToWait ?? 0, + ActionCode = letter.ActionCode.Trim() + }); + } + } + } + + return resultList + .GroupBy(x => x.NoticeType.Trim(), + (key, g) => g.OrderByDescending(x => x.DateSent).FirstOrDefault()) + .OrderByDescending(x => x.DateSent).ToList(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/SettingsService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/SettingsService.cs new file mode 100644 index 0000000..ecff922 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/SettingsService.cs @@ -0,0 +1,590 @@ +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Caching.Memory; +//using LCU.API.Internal.RepoApi.Repos.Models.Settings; +//using LCU.API.Internal.RepoApi.Repos.Repositories.Settings; +//using LCU.API.Internal.RepoApi.Repos.Repositories; +//using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Repos.Repositories.Settings; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Repos.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class SettingsService : ISettingsService +{ + private readonly IAccountTypeRepository _accountTypeRepository; + private readonly IAllowedStatusChangeRepository _allowedStatusChangeRepository; + private readonly IBodyStyleRepository _bodyStyleRepository; + private readonly ICleaningLevelRepository _cleaningLevelRepository; + private readonly ICollateralTypeRepository _collateralTypeRepository; + private readonly ICollateralTypeToVehicleTypeRepository _collateralTypeToVehicleTypeRepository; + private readonly IHoldStatusRepository _holdStatusRepository; + private readonly ILoanCategoryRepository _loanCategoryRepository; + private readonly ILocationTypeRepository _locationTypeRepository; + private readonly IVehicleTypeRepository _vehicleTypeRepository; + private readonly IExternalUserRepository _externalUserRepository; + private readonly IRepoCompanyRepository _repoCompanyRepository; + private readonly IUnitOfWork _unitOfWork; + private readonly IMemoryCache _memoryCache; + private readonly ICollectionsDao _collectionsDao; + private readonly ApiConfiguration _apiConfiguration; + + public SettingsService( + IAccountTypeRepository accountTypeCodesRepository, + IAllowedStatusChangeRepository allowedStatusChangesRepository, + IBodyStyleRepository bodyStyleCodesRepository, + ICleaningLevelRepository cleaningLevelCodesRepository, + ICollateralTypeRepository collateralTypesRepository, + ICollateralTypeToVehicleTypeRepository collateralTypesToVehicleTypesRepository, + IHoldStatusRepository holdStatusCodesRepository, + ILoanCategoryRepository loanCategoriesRepository, + ILocationTypeRepository locationTypeCodesRepository, + IVehicleTypeRepository vehicleTypeCodesRepository, + IExternalUserRepository externalUserRepository, + IRepoCompanyRepository repoCompanyRepository, + IUnitOfWork unitOfWork, IMemoryCache memoryCache, + ICollectionsDao collectionsDao, + ApiConfiguration apiConfiguration) + { + _accountTypeRepository = accountTypeCodesRepository; + _allowedStatusChangeRepository = allowedStatusChangesRepository; + _bodyStyleRepository = bodyStyleCodesRepository; + _cleaningLevelRepository = cleaningLevelCodesRepository; + _collateralTypeRepository = collateralTypesRepository; + _collateralTypeToVehicleTypeRepository = collateralTypesToVehicleTypesRepository; + _holdStatusRepository = holdStatusCodesRepository; + _loanCategoryRepository = loanCategoriesRepository; + _locationTypeRepository = locationTypeCodesRepository; + _vehicleTypeRepository = vehicleTypeCodesRepository; + _externalUserRepository = externalUserRepository; + _repoCompanyRepository = repoCompanyRepository; + _unitOfWork = unitOfWork; + _memoryCache = memoryCache; + _collectionsDao = collectionsDao; + _apiConfiguration = apiConfiguration; + } + + public async Task GetCachedRepoSettingsAsync() + { + return await _memoryCache.GetOrCreateAsync("repoSettings", factory: (entry) => + { + entry.AbsoluteExpiration = DateTime.Now.AddSeconds(_apiConfiguration.SettingsCacheExpirationSeconds); + return ListAllSettingsAsync(); + }); + } + + public async Task ListAllSettingsAsync() + { + try + { + var result = new Settings + { + AccountTypes = await _accountTypeRepository.ListAllAsync(), + AllowedStatusChanges = await _allowedStatusChangeRepository.ListAllAsync(), + BodyStyles = await _bodyStyleRepository.ListAllAsync(), + CleaningLevels = await _cleaningLevelRepository.ListAllAsync(), + CollateralTypes = await _collateralTypeRepository.ListAllAsync(), + CollateralTypesToVehicleTypes = await _collateralTypeToVehicleTypeRepository.ListAllAsync(), + HoldStatuses = await _holdStatusRepository.ListAllAsync(), + //TODO: Why is loan categories called both here, and as part of the accounttypes? + LoanCategories = await _loanCategoryRepository.ListAllAsync(), + LocationTypes = await _locationTypeRepository.ListAllAsync(), + VehicleTypes = await _vehicleTypeRepository.ListAllAsync(), + BranchSet = await _collectionsDao.RetrieveBranchSetAsync(), + ExternalUsers = await _externalUserRepository.ListAsync(), + RepoCompanies = await _repoCompanyRepository.ListAsync(), + TransactionTypeCodes = _apiConfiguration.TransactionTypeCodes, + TransactionTypeCodeQueryPrefix = _apiConfiguration.TransactionTypeCodeQueryPrefix, + // Convert DateTime format to uppercase for React/Moment JS ToString use + TransactionHistoryParameterDateFormat = _apiConfiguration.TransactionHistoryParameterDateFormat.ToUpper(), + TransactionHistoryStartDateOffset = _apiConfiguration.TransactionHistoryStartDateOffset, + RepoTypeMap = GetRepoTypes() + }; + + return result; + } + catch (Exception ex) + { + Log.Error(ex, "Failure retrieving Settings."); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(AccountType accountType) + { + try + { + await _accountTypeRepository.AddAsync(accountType); + await _unitOfWork.CompleteAsync(); + + return accountType; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Account Type: {accountType}", accountType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(AllowedStatusChange statusChange) + { + try + { + await _allowedStatusChangeRepository.AddAsync(statusChange); + await _unitOfWork.CompleteAsync(); + + return statusChange; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Allowed Status Change: {statusChange}", statusChange); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(BodyStyle bodyStyle) + { + try + { + await _bodyStyleRepository.AddAsync(bodyStyle); + await _unitOfWork.CompleteAsync(); + + return bodyStyle; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Body Style: {bodyStyle}", bodyStyle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(CleaningLevel cleaningLevel) + { + try + { + await _cleaningLevelRepository.AddAsync(cleaningLevel); + await _unitOfWork.CompleteAsync(); + + return cleaningLevel; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Cleaning Level: {cleaningLevel}", cleaningLevel); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(CollateralType collateralType) + { + try + { + await _collateralTypeRepository.AddAsync(collateralType); + await _unitOfWork.CompleteAsync(); + + return collateralType; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Collateral: {collateralType}", collateralType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(CollateralTypeToVehicleType collateralToVehicle) + { + try + { + await _collateralTypeToVehicleTypeRepository.AddAsync(collateralToVehicle); + await _unitOfWork.CompleteAsync(); + + return collateralToVehicle; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Collateral Type to Vehicle Type: {collateralToVehicle}", + collateralToVehicle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(HoldStatus holdStatus) + { + try + { + await _holdStatusRepository.AddAsync(holdStatus); + await _unitOfWork.CompleteAsync(); + + return holdStatus; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Hold Status: {holdStatus}", holdStatus); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(LoanCategory loanCategory) + { + try + { + await _loanCategoryRepository.AddAsync(loanCategory); + await _unitOfWork.CompleteAsync(); + + return loanCategory; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Loan Category: {loanCategory}", loanCategory); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(LocationType locationType) + { + try + { + await _locationTypeRepository.AddAsync(locationType); + await _unitOfWork.CompleteAsync(); + + return locationType; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Location Type: {locationType}", locationType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SaveAsync(VehicleType vehicleType) + { + try + { + await _vehicleTypeRepository.AddAsync(vehicleType); + await _unitOfWork.CompleteAsync(); + + return vehicleType; + } + catch (Exception ex) + { + Log.Error(ex, $"Failure saving Vehicle Type: {vehicleType}", vehicleType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(AccountType accountType) + { + var existingAccountType = await _accountTypeRepository.FindByIdAsync(accountType.ID); + + if (existingAccountType == null) + { + throw new NotFoundException($" Account Type with id {accountType.ID} not found."); + } + + existingAccountType.AccountTypeCode = accountType.AccountTypeCode; + existingAccountType.Description = accountType.Description; + existingAccountType.IsVisible = accountType.IsVisible; + + try + { + _accountTypeRepository.Update(existingAccountType); + await _unitOfWork.CompleteAsync(); + return existingAccountType; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Account Type: {accountType}", accountType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Account Type: {accountType}", accountType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(AllowedStatusChange allowedStatusChange) + { + var existingStatusChange = await _allowedStatusChangeRepository.FindByIdAsync(allowedStatusChange.ID); + + if (existingStatusChange == null) + { + throw new NotFoundException($" Allowed Status Change with id {allowedStatusChange.ID} not found."); + } + + existingStatusChange.CurrentStatus = allowedStatusChange.CurrentStatus; + existingStatusChange.AllowedChangeStatus = allowedStatusChange.AllowedChangeStatus; + + try + { + _allowedStatusChangeRepository.Update(existingStatusChange); + await _unitOfWork.CompleteAsync(); + return existingStatusChange; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Allowed Status Change: {allowedStatusChange}", + allowedStatusChange); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Allowed Status Change {allowedStatusChange}", allowedStatusChange); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(BodyStyle bodyStyle) + { + var existingBodyStyle = await _bodyStyleRepository.FindByIdAsync(bodyStyle.Code); + + if (existingBodyStyle == null) + { + throw new NotFoundException($" Body Style with code {bodyStyle.Code} not found."); + } + + existingBodyStyle.Description = bodyStyle.Description; + + try + { + _bodyStyleRepository.Update(existingBodyStyle); + await _unitOfWork.CompleteAsync(); + return existingBodyStyle; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Body Style: {bodyStyle}", bodyStyle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Body Style: {bodyStyle}", bodyStyle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(CleaningLevel cleaningLevel) + { + var existingCleaningLevel = await _cleaningLevelRepository.FindByIdAsync(cleaningLevel.Code); + + if (existingCleaningLevel == null) + { + throw new NotFoundException($" Cleaning Level with code {cleaningLevel.Code} not found."); + } + + existingCleaningLevel.Description = cleaningLevel.Description; + + try + { + _cleaningLevelRepository.Update(existingCleaningLevel); + await _unitOfWork.CompleteAsync(); + return existingCleaningLevel; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Cleaning Level: {cleaningLevel}", cleaningLevel); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Cleaning Level: {cleaningLevel}", cleaningLevel); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(CollateralType collateralType) + { + var existingCollateralType = await _collateralTypeRepository.FindByIdAsync(collateralType.ID); + + if (existingCollateralType == null) + { + throw new NotFoundException($" Collateral Type with code {collateralType.ID} not found."); + } + + existingCollateralType.Description = collateralType.Description; + existingCollateralType.LookupHandler = collateralType.LookupHandler; + + try + { + _collateralTypeRepository.Update(existingCollateralType); + await _unitOfWork.CompleteAsync(); + return existingCollateralType; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Collateral Type: {collateralType}", collateralType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Collateral Type: {collateralType}", collateralType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(CollateralTypeToVehicleType collateralToVehicle) + { + var existingCollateralToVehicle = + await _collateralTypeToVehicleTypeRepository.FindByIdAsync(collateralToVehicle.ID); + + if (existingCollateralToVehicle == null) + { + throw new NotFoundException( + $" Collateral Type To Vehicle Type with id {collateralToVehicle.ID} not found."); + } + + existingCollateralToVehicle.CollateralTypeId = collateralToVehicle.CollateralTypeId; + existingCollateralToVehicle.VehicleTypeId = collateralToVehicle.VehicleTypeId; + + try + { + _collateralTypeToVehicleTypeRepository.Update(existingCollateralToVehicle); + await _unitOfWork.CompleteAsync(); + return existingCollateralToVehicle; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Collateral Type To Vehicle Type: {collateralToVehicle}", + collateralToVehicle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Collateral Type To Vehicle Type: {collateralToVehicle}", + collateralToVehicle); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(HoldStatus holdStatus) + { + var existingHoldStatus = await _holdStatusRepository.FindByIdAsync(holdStatus.Code); + + if (existingHoldStatus == null) + { + throw new NotFoundException($" Hold Status with code {holdStatus.Code} not found."); + } + + existingHoldStatus.Description = holdStatus.Description; + + try + { + _holdStatusRepository.Update(existingHoldStatus); + await _unitOfWork.CompleteAsync(); + return existingHoldStatus; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Hold Status: {holdStatus}", holdStatus); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Hold Status: {holdStatus}", holdStatus); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(LoanCategory loanCategory) + { + var existingLoanCategory = await _loanCategoryRepository.FindByIdAsync(loanCategory.ID); + + if (existingLoanCategory == null) + { + throw new NotFoundException($" Loan Category with code {loanCategory.ID} not found."); + } + + existingLoanCategory.CategoryName = loanCategory.CategoryName; + + try + { + _loanCategoryRepository.Update(existingLoanCategory); + await _unitOfWork.CompleteAsync(); + return existingLoanCategory; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Loan Category: {loanCategory}", loanCategory); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Loan Category: {loanCategory}", loanCategory); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(LocationType locationType) + { + var existingLocationType = await _locationTypeRepository.FindByIdAsync(locationType.Code); + + if (existingLocationType == null) + { + throw new NotFoundException($" Location Type with code {locationType.Code} not found."); + } + + existingLocationType.Description = locationType.Description; + + try + { + _locationTypeRepository.Update(existingLocationType); + await _unitOfWork.CompleteAsync(); + return existingLocationType; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Location Type: {locationType}", locationType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Location Type: {locationType}", locationType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task UpdateAsync(VehicleType vehicleType) + { + var existingVehicleType = await _vehicleTypeRepository.FindByIdAsync(vehicleType.Code); + + if (existingVehicleType == null) + { + throw new NotFoundException($" Vehicle Type with code {vehicleType.Code} not found."); + } + + existingVehicleType.Description = vehicleType.Description; + + try + { + _vehicleTypeRepository.Update(existingVehicleType); + await _unitOfWork.CompleteAsync(); + return existingVehicleType; + } + catch (SqlException ex) + { + Log.Error(ex, $"SQL Failure updating Vehicle Type: {vehicleType}", vehicleType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + catch (Exception ex) + { + Log.Error(ex, $"Failure updating Vehicle Type: {vehicleType}", vehicleType); + throw new Old_Repo_Exceptions.ServerErrorException(); + } + } + + public async Task SyncExternalUsersAsync(IReadOnlyCollection users) + { + await _externalUserRepository.SynchronizeSet(users); + await _unitOfWork.CompleteAsync(); + } + + private static Dictionary GetRepoTypes() + { + return Enum.GetValues(typeof(RepoType)) + .Cast() + .ToDictionary(type => (int)type, type => type.ToString()); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/StatusService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/StatusService.cs new file mode 100644 index 0000000..55e82d0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Old_Repo_Services/StatusService.cs @@ -0,0 +1,33 @@ +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Collections.CollectionsApiLib.Models; +namespace LCU.API.Internal.RepoApi.Old_Repo_Services; + +public class StatusService +{ + /** + * Convert a Stage from a case to a RepoAccount status. + */ + public static string ConvertStatus(RepoStatus oldStage) + { + switch (oldStage) + { + case RepoStatus.PSKP: return "PSKP"; + case RepoStatus.REHO: return "REHO"; + case RepoStatus.RECA: return "RECA"; + case RepoStatus.O4R: return "O4R"; + case RepoStatus.REPO: return "REPO"; + case RepoStatus.REDEEMED: return "REDEEMED"; + case RepoStatus.SOLD: return "SOLD"; + case RepoStatus.NOTACTIVE: return "SOLD"; + case RepoStatus.INELIGIBLE: throw new IneligibleAccountException("CaseStage", "Not a Repo Case Stage."); + // case RepoStatus.UNKNOWN is meant to fall through to below. + } + + Log.Fatal($"Attempt to convert status {oldStage}, but was not known."); + // The account is not necessarily ineligible, but is missing a requirement, the stage. + // This covers the problem of Temenos not providing a Case Stage Change event, and only + // a Case Creation event. The stage won't be available yet, so throw a MissingRequirement + // causing it to be retried on the hour - it should then run fine. + throw new MissingRequirementException("Case Stage", $"A valid case stage is required, {oldStage} is not valid."); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Program.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Program.cs new file mode 100644 index 0000000..c94feb9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Program.cs @@ -0,0 +1,366 @@ +using LCU.Library.Internal.Configuration; +using LCU.Library.Internal.Logging; +using LCU.Library.Internal.Logging.Middleware; +using LCU.Library.Internal.Logging.Models; +using LCU.Library.Internal.Status; +using LCU.API.Internal.RepoApi.Configurations; +using Microsoft.OpenApi.Models; +using System.Text.Json.Serialization; +using LCU.Library.Internal.LcuMassTransit; +using LCU.Library.Internal.LcuMassTransitModels; +using MassTransit; +using LCU.API.Internal.RepoApi.Consumers; +using LCU.API.Collections.RepoApi.Configuration.Refactor_Temp; +using LCU.API.Internal.RepoApi.Old_Repo_Services.HostedServices; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; +using LCU.API.Internal.RepoApi.Old_Repo_Domain; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Services.RepoOperationServices; +using LCU.Api.Internal.RepoApi.Services.QueueServices; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using Microsoft.EntityFrameworkCore; +using LCU.Library.Internal.LcuHttpClient; +using LCU.API.Internal.RepoApi.Repos.Repositories.Batch; +using LCU.API.Internal.RepoApi.Services.QueueServices; +using LCU.API.Internal.RepoApi.Services.Email; +using LCU.API.Internal.RepoApi.Services; +using LCU.Library.Internal.LcuClassPatterns.Crud; + + +string MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; + +var builder = BuildBuilder(args); +BuildServicesAndConfigurations(builder); +BuildAppAndRun(builder); + +WebApplicationBuilder BuildBuilder(string[] args) +{ + var builder = WebApplication.CreateBuilder(args); + builder.Configuration.AddJsonFile("/app/config/environment-appsettings/appsettings.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/app/config/workload-appsettings/appsettings.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/repo-api-seq-api-key.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/repo-api-database-connection-string.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/repo-api-queue-user-name.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/repo-api-queue-password.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/repoapi_configuration.json", optional: true, reloadOnChange: true); + builder.Host.UseSerilog(); + return builder; +} + +void BuildServicesAndConfigurations(WebApplicationBuilder builder) +{ + var services = builder.Services; + var configuration = builder.Configuration; + + var apiConfiguration = configuration.Bind(services, "Primary"); + configuration.Bind(services); + configuration.Bind(services); + configuration.Bind(services); + + var loggerConfiguration = new LoggingConfiguration(apiConfiguration.AppName, + apiConfiguration.SeqApiKey, + apiConfiguration.SeqUrl); + var lcuLogging = new LcuLogging(loggerConfiguration); + lcuLogging.UseLcuLogging(); + + services.AddCors(options => + { + options.AddPolicy(MyAllowSpecificOrigins, + builder => + { + builder.WithOrigins( + apiConfiguration.CorsAllowedOrigins + ).AllowAnyHeader().AllowAnyMethod(); + }); + }); + + var databaseSettings = configuration.Bind(services); + + services.AddDbContext(options => + { + options.UseSqlServer + ( + databaseSettings.ConnectionString, + sql => sql.EnableRetryOnFailure(databaseSettings.MaxRetryCount, databaseSettings.MaxRetryDelay, null) + ); + }); + + var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration); + + var receiveEndpointConfigurations = new ReceiveEndpointConfigurations() + { + RepoAccountProcessingReceiveEndpoint = + MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "RepoAccountProcessingReceiveEndpoint"), + RepoBatchProcessingReceiveEndpoint = + MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "RepoBatchProcessingReceiveEndpoint"), + RepoBatchCompletionProcessingReceiveEndpoint = + MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "RepoBatchCompletionProcessingReceiveEndpoint") + }; + + services.AddSingleton(receiveEndpointConfigurations); + AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfigurations, apiConfiguration); + + services.AddLCUStatusServicesWithApiHealthCheck(configuration); + services.AddMemoryCache(); + //services.AddScoped(); + + AddApiServices(services); + AddOldRepoDomains(services); + AddOldRepoDataAccess(services); + AddOldRepoRepositories(services); + AddNewRepoRepositories(services); + + services.AddLCUHttpServices(); + services.AddLcuCrudServices(); + + services.AddScoped(); + + services.AddHttpClient(); + + services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{apiConfiguration.AppName}", Version = "v1" }); + }); +} + +void AddMassTransitServices(ref IServiceCollection services, + ConnectionConfiguration connectionConfiguration, + ReceiveEndpointConfigurations receiveEndpointConfigurations, + ApiConfiguration configuration) +{ + services.AddMassTransit(x => + { + x.AddConsumer(); + x.AddConsumer(); + x.AddConsumer(); + + x.UsingRabbitMq((context, cfg) => + { + cfg.Host(connectionConfiguration.HostUrl, "/", host => + { + host.Username(connectionConfiguration.UserName); + host.Password(connectionConfiguration.Password); + }); + cfg.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + + if (configuration.FeatureFlags.RunRepoAccountProcessingConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.RepoAccountProcessingReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.RepoAccountProcessingReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + if (configuration.FeatureFlags.RunRepoBatchProcessingConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.RepoBatchProcessingReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.RepoBatchProcessingReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + if (configuration.FeatureFlags.RunRepoBatchCompletionProcessingConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.RepoBatchCompletionProcessingReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.RepoBatchCompletionProcessingReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + }); + }); + + services.AddMassTransitHostedService(); +} + +void AddApiServices(IServiceCollection services) +{ + AddEmailServices(services); + AddQueueServices(services); + AddRepoOperationServices(services); + AddOldRepoServices(services); +} + +void AddQueueServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddEmailServices(IServiceCollection services) +{ + services.AddScoped(); +} + +void AddRepoOperationServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddOldRepoServices(IServiceCollection services) +{ + //"HostedServices" + services.AddScoped(); + //"Producer" + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //Services + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddOldRepoDomains(IServiceCollection services) +{ + AddOldRepoDomainAlertCheckers(services); + services.AddScoped(); +} + +void AddOldRepoDomainAlertCheckers(IServiceCollection services) +{ + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); +} + +void AddOldRepoRepositories(IServiceCollection services) +{ + services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + //services.AddScoped(); +} + +void AddNewRepoRepositories(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddOldRepoDataAccess(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); +} + +void BuildAppAndRun(WebApplicationBuilder builder) +{ + var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseSwagger(c => + { + c.PreSerializeFilters.Add((swaggerDoc, request) => + { + const string prefixHeader = "X-Forwarded-Prefix"; + if (!request.Headers.ContainsKey(prefixHeader)) return; + + var serverUrl = request.Headers[prefixHeader]; + if (!string.IsNullOrWhiteSpace(serverUrl)) + { + swaggerDoc.Servers = new List + { + new() { Description = serverUrl, Url = serverUrl } + }; + } + }); + }); + app.UseSwaggerUI(c => + { + c.RoutePrefix = "swagger"; + c.SwaggerEndpoint("v1/swagger.json", "LCU.API.Internal.RepoApi v1"); + }); + app.UseMiddleware(); + app.UseSerilogRequestLogging(options => + { + options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest; + }); + + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseCors(MyAllowSpecificOrigins); + app.UseHttpsRedirection(); + app.MapControllers(); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + + app.Run(); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Properties/launchSettings.json b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Properties/launchSettings.json new file mode 100644 index 0000000..1fc3a11 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:28861", + "sslPort": 44350 + } + }, + "profiles": { + "API": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7066;http://localhost:5157", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/EmailService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/EmailService.cs new file mode 100644 index 0000000..5669ee0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/EmailService.cs @@ -0,0 +1,68 @@ +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Services.RepoOperationServices; +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Services.Email; + +public class EmailService : IEmailService +{ + private readonly IHttpClientFactory _httpClientFactory; + private readonly ApiConfiguration _apiConfiguration; + private readonly NotificationApiConfiguration _notificationApiConfiguration; + private readonly IEventAlertService _eventAlertService; + private readonly IEmailer _emailer; + private readonly IRepoSyncBatchService _repoSyncBatchService; + private readonly IRepoAccountService _repoAccountService; + private const string O4RStatus = "O4R"; + + public EmailService(IHttpClientFactory httpClientFactory, ApiConfiguration apiConfiguration, NotificationApiConfiguration notificationApiConfiguration, IEventAlertService eventAlertService, IEmailer emailer, IRepoSyncBatchService repoSyncBatchService, IRepoAccountService repoAccountService) + { + _httpClientFactory = httpClientFactory; + _apiConfiguration = apiConfiguration; + _notificationApiConfiguration = notificationApiConfiguration; + _eventAlertService = eventAlertService; + _emailer = emailer; + _repoSyncBatchService = repoSyncBatchService; + _repoAccountService = repoAccountService; + } + + public async Task SendProccessSummary(RepoSyncRunInfo runInfo) + { + + string subject = $"Repo Processor Run Receipt [{runInfo.RepoSyncCompletedCount}]"; + + var accounts = _repoSyncBatchService.GetAllByRunInfoIdAsync(runInfo.RepoSyncRunInfoId).Result; + + var receiptList = accounts.Select(receipt => receipt.Pretty()); + string bodyData = string.Join("
", receiptList); + string body = $"Process Started: {runInfo.RepoStartDateTime} and completed at {runInfo.RepoEndDateTime}

"; + + return await _emailer.SendMessageAsync(_apiConfiguration.RepoManagerAdminRecipients, subject, body + bodyData); + } + + public async Task SendAlertSummaryEmailWithEndTime(DateTime endTime) + { + var startTime = endTime.AddHours(-1); + var eventAlerts = _eventAlertService.ListWithinDateRange(startTime, endTime).GetAwaiter().GetResult(); + var subject = "Repo Manager Alerts " + endTime.ToString("MM-dd-yyyy HH:mm:ss"); + var body = "

" + subject + "


"; + + foreach (var eventAlert in eventAlerts) + { + var entry = _emailer.CreateAlertEmailEntry(eventAlert); + if (!string.IsNullOrEmpty(entry)) + { + body += _emailer.CreateAlertEmailEntry(eventAlert) + "

"; + } + } + + if (!eventAlerts.Any()) + { + body += "No new alerts."; + } + + return await _emailer.SendMessageAsync(_apiConfiguration.CollectionsRecipients, subject, body); + } + +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/IEmailService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/IEmailService.cs new file mode 100644 index 0000000..6906851 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/Email/IEmailService.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Services.Email; + +public interface IEmailService +{ + Task SendProccessSummary(RepoSyncRunInfo runInfo); + Task SendAlertSummaryEmailWithEndTime(DateTime endTime); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncBatchService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncBatchService.cs new file mode 100644 index 0000000..8b78ea8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncBatchService.cs @@ -0,0 +1,19 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.RepoApiModels.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LCU.API.Internal.RepoApi.Services +{ + public interface IRepoSyncBatchService: ICrudService + { + Task> GetAllByRunInfoIdAsync(int id); + Task GetByRepoSyncBatchIdAsync(int id); + Task GetByBatchIdAndAuditIdAsync(int batchId, string auditId); + Task GetUncompletedByAuditId(string auditId); + Task CompleteAsync(RepoSyncBatch repoSyncBatch, string outcome = null, string message = null); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncRunInfoService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncRunInfoService.cs new file mode 100644 index 0000000..6afbd86 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/IRepoSyncRunInfoService.cs @@ -0,0 +1,10 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Services +{ + public interface IRepoSyncRunInfoService: ICrudService + { + Task GetByRepoSyncRunInfoIdAsync(int id); + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoAccountProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoAccountProcessingService.cs new file mode 100644 index 0000000..3b9c836 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoAccountProcessingService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.Api.Internal.RepoApi.Services.QueueServices; + +public interface IQueueRepoAccountProcessingService +{ + Task QueueRepoAccountProcessing(List request); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoBatchProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoBatchProcessingService.cs new file mode 100644 index 0000000..8963d76 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoBatchProcessingService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.Api.Internal.RepoApi.Services.QueueServices; + +public interface IQueueRepoBatchProcessingService +{ + Task QueueRepoBatchInitiate(RepoBatchRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoCompletionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoCompletionService.cs new file mode 100644 index 0000000..d848925 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/IQueueRepoCompletionService.cs @@ -0,0 +1,9 @@ +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Services.QueueServices; + +public interface IQueueRepoCompletionService +{ + Task QueueRepoBatchCompletion(RepoSyncBatch request); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoAccountProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoAccountProcessingService.cs new file mode 100644 index 0000000..9ef20c9 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoAccountProcessingService.cs @@ -0,0 +1,46 @@ +using MassTransit; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.Api.Internal.RepoApi.Services.QueueServices; + +public class QueueRepoAccountProcessingService : IQueueRepoAccountProcessingService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueRepoAccountProcessingService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.RepoAccountProcessingReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueRepoAccountProcessing(List request) + { + try + { + var tasks = request.Select(request => QueueTransactionProcessingRequest(request)); + + await Task.WhenAll(tasks); + } + catch (Exception exception) + { + var message = $"Failed to Queue RepoAccountProcessing"; + Log.Error($"{message}, Exception Message: {exception.Message}"); + throw new ServiceException($"{message}, Exception Message: {exception.Message}"); + } + } + + public async Task QueueTransactionProcessingRequest(RepoAccountProcessingRequest request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception ex) + { + var errorMessage = $"Error during QueueTransactionProcessingRequest. Creating Failed TransactionAlertRecord. Error Message: {ex.Message}"; + Log.Warning(errorMessage); + } + } + +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoBatchProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoBatchProcessingService.cs new file mode 100644 index 0000000..5b7c562 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoBatchProcessingService.cs @@ -0,0 +1,29 @@ +using MassTransit; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.Api.Internal.RepoApi.Services.QueueServices; + +public class QueueRepoBatchProcessingService : IQueueRepoBatchProcessingService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueRepoBatchProcessingService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.RepoBatchProcessingReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueRepoBatchInitiate(RepoBatchRequest request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception ex) + { + var errorMessage = $"Error during QueueRepoBatchRequest. Creating Failed QueueRepoBatchRequest. Error Message: {ex.Message}"; + Log.Warning(errorMessage); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoCompletionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoCompletionService.cs new file mode 100644 index 0000000..f7fbec2 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/QueueServices/QueueRepoCompletionService.cs @@ -0,0 +1,31 @@ +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.Library.Internal.RepoApiModels.Models; +using MassTransit; + +namespace LCU.API.Internal.RepoApi.Services.QueueServices; + +public class QueueRepoCompletionService : IQueueRepoCompletionService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueRepoCompletionService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.RepoBatchCompletionProcessingReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueRepoBatchCompletion(RepoSyncBatch request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception ex) + { + var errorMessage = $"Error during QueueRepoBatchCompletionRequest. Creating Failed QueueRepoBatchCompletion. Error Message: {ex.Message}"; + Log.Warning(errorMessage); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoAccountProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoAccountProcessingService.cs new file mode 100644 index 0000000..5c6ad97 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoAccountProcessingService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public interface IRepoAccountProcessingService +{ + Task HandleRequest(RepoAccountProcessingRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchCompletionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchCompletionService.cs new file mode 100644 index 0000000..b5bf6a6 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchCompletionService.cs @@ -0,0 +1,8 @@ +using LCU.Library.Internal.RepoApiModels.Models; + +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public interface IRepoBatchCompletionService +{ + Task HandleRequest(List batchList); +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchProcessingService.cs new file mode 100644 index 0000000..9906990 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/IRepoBatchProcessingService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.RepoApiModels.Models.Requests; +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public interface IRepoBatchProcessingService +{ + Task HandleRequest(RepoBatchRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoAccountProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoAccountProcessingService.cs new file mode 100644 index 0000000..6624394 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoAccountProcessingService.cs @@ -0,0 +1,84 @@ +using LCU.Library.Internal.RepoApiModels.Models; +using LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Services.QueueServices; +using Polly; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using LCU.Library.Internal.Logging.Performance; +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public class RepoAccountProcessingService : IRepoAccountProcessingService +{ + private readonly IRepoAccountProcessor _repoAccountProcessor; + private readonly ISettingsService _settingsService; + private readonly IRepoSyncBatchService _repoSyncBatchService; + private readonly IQueueRepoCompletionService _queueRepoCompletionService; + private readonly IProcessingReceiptService _processingReceiptService; + + public RepoAccountProcessingService(ISettingsService settingsService, IRepoAccountProcessor repoAccountProcessor, IRepoSyncBatchService repoSyncBatchService, IQueueRepoCompletionService queueRepoCompletionService, IProcessingReceiptService processingReceiptService) + { + _repoAccountProcessor = repoAccountProcessor; + _settingsService = settingsService; + _repoSyncBatchService = repoSyncBatchService; + _queueRepoCompletionService = queueRepoCompletionService; + _processingReceiptService = processingReceiptService; + } + + public async Task HandleRequest(RepoAccountProcessingRequest request) + { + try + { + Log.Information("AccountProcessing Process Starting"); + var performanceMonitorId = Guid.NewGuid(); + var handleAccountProcessingRequestMonitor = new PerformanceMonitor("HandleAccountProcessingRequest", performanceMonitorId); + + var receipt = new ProcessingReceipt(request.AccountNo) + { + AccountId = request.AccountId ?? "", + CaseId = request.CaseId ?? "", + QueuedBy = request.QueuedBy + }; + + var newRequest = new ProcessingItem() + { + AccountNo = request.AccountNo, + AccountId = request.AccountId, + CaseId = request.CaseId, + QueuedBy = request.QueuedBy + }; + + receipt.Outcome = await _repoAccountProcessor.HandleAccount(newRequest); + if (receipt.Message == null && receipt.Outcome.Equals(OutcomeType.Successful)) + { + receipt.Message = "Success"; + } + + await _processingReceiptService.SaveAsync(receipt); + + RepoSyncBatch repoBatch = _repoSyncBatchService.GetUncompletedByAuditId(request.DetermineAuditItemKey()).Result; + + if (repoBatch != null) + { + repoBatch = await _repoSyncBatchService.CompleteAsync(repoBatch, receipt.Outcome.ToString(), receipt.Message); + await _queueRepoCompletionService.QueueRepoBatchCompletion(repoBatch); + } + + handleAccountProcessingRequestMonitor.LogPerformance(); + + Log.Information("AccountProcessing Process Completed Successfully"); + } + catch (Exception ex) + { + var errorMessage = $"Error during AccountProcessing. Error Message: {ex.Message}"; + RepoSyncBatch repoBatch = _repoSyncBatchService.GetUncompletedByAuditId(request.DetermineAuditItemKey()).Result; + if (repoBatch != null) + { + repoBatch = await _repoSyncBatchService.CompleteAsync(repoBatch, "Failed", errorMessage); + } + await _queueRepoCompletionService.QueueRepoBatchCompletion(repoBatch); + throw new ServiceException(errorMessage); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchCompletionService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchCompletionService.cs new file mode 100644 index 0000000..9d3502c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchCompletionService.cs @@ -0,0 +1,105 @@ +using LCU.API.Internal.RepoApi.Services.Email; +using LCU.Library.Internal.ApiResponse.Services; +using LCU.Library.Internal.RepoApiModels.Models; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using Newtonsoft.Json; + +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public class RepoBatchCompletionService: IRepoBatchCompletionService +{ + private readonly IRepoSyncRunInfoService _repoSyncRunInfoService; + private readonly IRepoSyncBatchService _repoSyncBatchService; + private readonly IEmailService _emailService; + + public RepoBatchCompletionService(IRepoSyncRunInfoService repoSyncRunInfoService, IRepoSyncBatchService repoSyncBatchService, IEmailService emailService) + { + _repoSyncRunInfoService = repoSyncRunInfoService; + _repoSyncBatchService = repoSyncBatchService; + _emailService = emailService; + } + + public async Task HandleRequest(List batchList) + { + try + { + var runInfoGroups = batchList.GroupBy(p => p.RepoSyncRunInfoId, + p => p.RepoSyncBatchId, + (key, g) => new { RepoSyncRunInfoId = key, RepoSyncBatchId = g.ToList() }); + + + foreach (var runInfo in runInfoGroups) + { + RepoSyncRunInfo existingRunInfo = await _repoSyncRunInfoService.GetByRepoSyncRunInfoIdAsync(runInfo.RepoSyncRunInfoId); + + existingRunInfo.RepoSyncCompletedCount = existingRunInfo.RepoSyncCompletedCount + runInfo.RepoSyncBatchId.Count; + + if (existingRunInfo.RepoSyncCompletedCount == existingRunInfo.RepoSyncTotalCount) + { + await CompleteBatch(existingRunInfo); + return; + } + + await _repoSyncRunInfoService.UpdateAsync(existingRunInfo); + } + } + catch (Exception ex) + { + var errorMessage = $"Error during Batch Completion. Error Message: {ex.Message}"; + throw new ServiceException(errorMessage); + } + } + + private async Task CompleteBatch(RepoSyncRunInfo runInfo) + { + try + { + runInfo.RepoEndDateTime = DateTime.Now; + + await _repoSyncRunInfoService.UpdateAsync(runInfo); + + await SendErrorNotification(runInfo); + await SendAlertNotification(runInfo); + + await QueueReturnResponse.QueueResponse(JsonConvert.DeserializeObject(runInfo.InitialRequest), true, new List()); + } + catch (Exception ex) + { + var errorMessage = $"Error during Batch Completion. Error Message: {ex.Message}"; + var ErrorList = new List(); + ErrorList.Add(errorMessage); + await QueueReturnResponse.QueueResponse(JsonConvert.DeserializeObject(runInfo.InitialRequest), false, ErrorList); + throw new ServiceException(errorMessage); + } + } + + private async Task SendErrorNotification(RepoSyncRunInfo runInfo) + { + if (runInfo.RepoSyncTotalCount > 0 && runInfo.RepoSyncCompletedCount > 0) + { + try + { + await _emailService.SendProccessSummary(runInfo); + } + catch (Exception exception){ + Log.Error($"RepoBatchCompletionProcessingService failed to send error email. Emailer Response: {exception.Message} "); + } + } + } + + private async Task SendAlertNotification(RepoSyncRunInfo runInfo) + { + if (runInfo.RepoSyncTotalCount > 0 && runInfo.RepoSyncCompletedCount > 0) + { + try + { + await _emailService.SendAlertSummaryEmailWithEndTime(runInfo.RepoEndDateTime ?? DateTime.Now); + } + catch (Exception exception) + { + Log.Error($"RepoBatchCompletionProcessingService failed to send error email. Emailer Response: {exception.Message} "); + } + } + } + +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchProcessingService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchProcessingService.cs new file mode 100644 index 0000000..a5a3d69 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoOperationServices/RepoBatchProcessingService.cs @@ -0,0 +1,137 @@ +using LCU.Library.Internal.RepoApiModels.Models; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.Library.Internal.ApiResponse.Services; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.Api.Internal.RepoApi.Services.QueueServices; +using LCU.API.Internal.RepoApi.Utilities.Transformers; +using Newtonsoft.Json; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using LCU.Library.Internal.Logging.Performance; + +namespace LCU.API.Internal.RepoApi.Services.RepoOperationServices; + +public class RepoBatchProcessingService + : IRepoBatchProcessingService +{ + private readonly ApiConfiguration _configuration; + private readonly ISettingsService _settingsService; + private readonly ICollectionsDao _collectionsDao; + private readonly IRepoAccountService _repoAccountService; + private readonly IProcessingReceiptService _processingReceiptService; + private readonly IQueueRepoAccountProcessingService _queueRepoAccountProcessingService; + private readonly IRepoSyncRunInfoService _repoSyncRunInfoService; + private readonly IRepoSyncBatchService _repoSyncBatchService; + + public RepoBatchProcessingService(IRepoAccountService repoAcountService, + ApiConfiguration configuration, + ISettingsService settingsService, + ICollectionsDao collectionsDao, + IProcessingReceiptService processingReceiptService, + IQueueRepoAccountProcessingService queueRepoAccountProcessingService, + IRepoSyncBatchService repoSyncBatchService, + IRepoSyncRunInfoService repoSyncRunInfoService) + { + _configuration = configuration; + _settingsService = settingsService; + _collectionsDao = collectionsDao; + _processingReceiptService = processingReceiptService; + _repoAccountService = repoAcountService; + _queueRepoAccountProcessingService = queueRepoAccountProcessingService; + _repoSyncRunInfoService = repoSyncRunInfoService; + _repoSyncBatchService = repoSyncBatchService; + } + + public async Task HandleRequest(RepoBatchRequest request) + { + try + { + Log.Information("BatchProcessing Starting"); + + var performanceMonitorId = Guid.NewGuid(); + var handleBatchProcessingRequestMonitor = new PerformanceMonitor("HandleBatchProcessingRequest", performanceMonitorId); + + RepoSyncRunInfo repoSyncRunInfo = new RepoSyncRunInfo + { + RepoStartDateTime = DateTime.Now, + RepoEndDateTime = null, + RepoSyncCompletedCount = 0, + RepoSyncTotalCount = 0, + InitialRequest = JsonConvert.SerializeObject(request) + }; + + var reprocessItems = await _processingReceiptService.GetRetryListAsync(); + var watchStatusItems = await RetrieveRepoAccountsOfInterest(); + var combinedList = CreateCombinedList(reprocessItems, watchStatusItems); + + repoSyncRunInfo.RepoSyncTotalCount = combinedList.Count; + + var savedRunInfo = await _repoSyncRunInfoService.SaveAsync(repoSyncRunInfo); + + var repoSyncBatch = combinedList.ToList().ToRepoSyncBatch(savedRunInfo.RepoSyncRunInfoId); + + await _repoSyncBatchService.SaveRangeAsync(repoSyncBatch); + + _queueRepoAccountProcessingService.QueueRepoAccountProcessing(combinedList.ToList()).Wait(); + + handleBatchProcessingRequestMonitor.LogPerformance(); + + Log.Information("BatchProcessing Completed Successfully"); + } + catch (Exception ex) + { + var errorMessage = $"Error during BatchProcessing. Error Message: {ex.Message}"; + await QueueReturnResponse.QueueResponse(request, false, new List { errorMessage }); + throw new ServiceException(errorMessage); + } + } + + public async Task> RetrieveRepoAccountsOfInterest() + { + var repoAccountNos = new List(); + try + { + repoAccountNos.AddRange(await _repoAccountService.GetItemsForProcessing()); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to Retrieve Repo Accounts For Polling in RepoQueueProducerService"); + } + + return repoAccountNos; + } + + public IReadOnlyCollection CreateCombinedList(IReadOnlyCollection listPriority, + IReadOnlyCollection listSecondary) + { + var combinedList = new List(); + + if (listPriority != null && listPriority.Any()) + { + foreach (var processingItem in listPriority) + { + combinedList.Add(new RepoAccountProcessingRequest() + { + 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; + } + +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncBatchService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncBatchService.cs new file mode 100644 index 0000000..f2f3582 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncBatchService.cs @@ -0,0 +1,148 @@ +using LCU.API.Internal.RepoApi.Repos.Repositories.Batch; +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.RepoApiModels.Models; +using Microsoft.IdentityModel.Tokens; +using System.Data.SqlClient; + +namespace LCU.API.Internal.RepoApi.Services; + +public class RepoSyncBatchService : CrudService, IRepoSyncBatchService +{ + private readonly IRepoSyncBatchRepository _repoSyncBatchRepository; + private readonly Repos.Repositories.IUnitOfWork _unitOfWork; + + public RepoSyncBatchService(Repos.Repositories.IUnitOfWork unitOfWork, IRepoSyncBatchRepository repoSyncBatchRepository) : base(repoSyncBatchRepository) + { + _repoSyncBatchRepository = repoSyncBatchRepository; + _unitOfWork = unitOfWork; + } + + public async Task GetByRepoSyncBatchIdAsync(int id) + { + try + { + return await _repoSyncBatchRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + var message = $"Failure retrieving RepoSyncBatch with ID {id}"; + Log.Error(ex, message); + throw new ServiceException(message); + + } + } + + public async Task> GetAllByRunInfoIdAsync(int id) + { + try + { + return await _repoSyncBatchRepository.FindAllByRunInfoIdAsync(id); + } + catch (Exception ex) + { + var message = "Failure retrieving RepoSyncBatches"; + Log.Error(ex, message); + throw new ServiceException(message); + } + } + + public async Task GetByBatchIdAndAuditIdAsync(int batchId, string auditId) + { + try + { + return await _repoSyncBatchRepository.FindByBatchIdAndAuditIdAsync(batchId, auditId); + } + catch (Exception ex) + { + var message = $"Failure retrieving RepoSyncBatch with Batch ID {batchId} and Audit ID {auditId}"; + Log.Error(ex, message); + throw new ServiceException(message); + + } + } + + public async Task GetUncompletedByAuditId(string auditId) + { + try + { + return await _repoSyncBatchRepository.FindUncompletedByAuditId(auditId); + } + catch (Exception ex) + { + var message = $"Failure retrieving RepoSyncBatch with Audit ID: {auditId}"; + Log.Error(ex, message); + throw new ServiceException(message); + + } + } + + public override async Task UpdateAsync(RepoSyncBatch repoSyncBatch) + { + try + { + var existing = await _repoSyncBatchRepository.FindByIdAsync(repoSyncBatch.RepoSyncBatchId); + + if (existing == null) + { + throw new ServiceException($"RepoSyncBatch with ID {repoSyncBatch.RepoSyncBatchId} not found"); + } + + existing.RepoSyncRunInfoId = repoSyncBatch.RepoSyncRunInfoId; + existing.AuditItemKey = repoSyncBatch.AuditItemKey; + existing.RepoSyncCompletionDateTime = repoSyncBatch.RepoSyncCompletionDateTime; + + await _repoSyncBatchRepository.UpdateAsync(repoSyncBatch); + await _unitOfWork.CompleteAsync(); + + return existing; + } + catch (SqlException sqlEx) + { + var message = "Failure updating RepoSyncBatch"; + Log.Error(sqlEx, message); + throw new ServiceException(message); + } + catch (Exception ex) + { + var message = "Failure updating RepoSyncBatch"; + Log.Error(ex, message); + throw new ServiceException(message); + } + } + + public async Task CompleteAsync(RepoSyncBatch repoSyncBatch, string outcome = null, string message = null) + { + try + { + var existing = await _repoSyncBatchRepository.FindByIdAsync(repoSyncBatch.RepoSyncBatchId); + + if (existing == null) + { + throw new ServiceException($"RepoSyncBatch with ID {repoSyncBatch.RepoSyncBatchId} not found"); + } + + existing.RepoSyncRunInfoId = repoSyncBatch.RepoSyncRunInfoId; + existing.AuditItemKey = repoSyncBatch.AuditItemKey; + existing.RepoSyncCompletionDateTime = DateTime.Now; + existing.Outcome = repoSyncBatch.Outcome.IsNullOrEmpty() ? outcome : repoSyncBatch.Outcome; + existing.Message = repoSyncBatch.Message.IsNullOrEmpty() ? message : repoSyncBatch.Message; + + await _repoSyncBatchRepository.UpdateAsync(existing); + await _unitOfWork.CompleteAsync(); + + return existing; + } + catch (SqlException sqlEx) + { + var errorMessage = "Failure updating RepoSyncBatch"; + Log.Error(sqlEx, errorMessage); + throw new ServiceException(errorMessage); + } + catch (Exception ex) + { + var errorMessage = "Failure updating RepoSyncBatch"; + Log.Error(ex, errorMessage); + throw new ServiceException(errorMessage); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncRunInfoService.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncRunInfoService.cs new file mode 100644 index 0000000..1afcbfe --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Services/RepoSyncRunInfoService.cs @@ -0,0 +1,67 @@ +using LCU.Api.Internal.MaidBotLib.Repositories; +using LCU.API.Internal.RepoApi.Repos.Repositories.Batch; +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.RepoApiModels.Models; +using System.Data.SqlClient; + +namespace LCU.API.Internal.RepoApi.Services; + +public class RepoSyncRunInfoService : CrudService,IRepoSyncRunInfoService +{ + private readonly IRepoSyncRunInfoRepository _repoSyncRunInfoRepository; + private readonly Repos.Repositories.IUnitOfWork _unitOfWork; + + public RepoSyncRunInfoService(Repos.Repositories.IUnitOfWork unitOfWork, IRepoSyncRunInfoRepository repoSyncInfoRepository) : base(repoSyncInfoRepository) + { + _repoSyncRunInfoRepository = repoSyncInfoRepository; + _unitOfWork = unitOfWork; + } + + public async Task GetByRepoSyncRunInfoIdAsync(int id) + { + try + { + return await _repoSyncRunInfoRepository.FindByIdAsync(id); + } + catch (Exception ex) + { + var message = $"Failure retrieving RepoSyncRunInfo with ID {id}"; + Log.Error(ex, message); + throw new ServiceException(message); + + } + } + + public override async Task UpdateAsync(RepoSyncRunInfo repoSyncRunInfo) + { + try + { + var existing = await _repoSyncRunInfoRepository.FindByIdAsync(repoSyncRunInfo.RepoSyncRunInfoId); + if (existing == null) + { + throw new ServiceException($"RepoSyncRunInfo with ID {repoSyncRunInfo.RepoSyncRunInfoId} not found"); + } + + existing.RepoStartDateTime = repoSyncRunInfo.RepoStartDateTime; + existing.RepoEndDateTime = repoSyncRunInfo.RepoEndDateTime; + existing.RepoSyncCompletedCount = repoSyncRunInfo.RepoSyncCompletedCount; + existing.RepoSyncTotalCount = repoSyncRunInfo.RepoSyncTotalCount; + + await _repoSyncRunInfoRepository.UpdateAsync(existing); + await _unitOfWork.CompleteAsync(); + return existing; + } + catch (SqlException sqlEx) + { + var message = $"Failure updating RepoSyncRunInfo with ID {repoSyncRunInfo.RepoSyncRunInfoId}"; + Log.Error(sqlEx, message); + throw new ServiceException(message); + } + catch (Exception ex) + { + var message = $"Failure updating RepoSyncRunInfo with ID {repoSyncRunInfo.RepoSyncRunInfoId}"; + Log.Error(ex, message); + throw new ServiceException(message); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Utilities/Transformers/RepoSyncBatchModelTransformer.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Utilities/Transformers/RepoSyncBatchModelTransformer.cs new file mode 100644 index 0000000..056fe61 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/Utilities/Transformers/RepoSyncBatchModelTransformer.cs @@ -0,0 +1,21 @@ +using LCU.Library.Internal.RepoApiModels.Models; +using LCU.Library.Internal.RepoApiModels.Models.Requests; +using System.Runtime.CompilerServices; + +namespace LCU.API.Internal.RepoApi.Utilities.Transformers +{ + public static class RepoSyncBatchModelTransformer + { + public static List ToRepoSyncBatch(this List repoRequestList, int repoSyncRunInfoId) + { + return repoRequestList.ConvertAll(x => new RepoSyncBatch() + { + RepoSyncRunInfoId = repoSyncRunInfoId, + AuditItemKey = x.DetermineAuditItemKey(), + QueuedBy = x.QueuedBy, + AccountId = x.AccountId + }); + } + }; +} + diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/appsettings.json b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/appsettings.json new file mode 100644 index 0000000..c79a574 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/appsettings.json @@ -0,0 +1,111 @@ +{ + "Primary": { + "AppName": "Repo Api", + "SeqUrl": "https://qa-seq.lcu-internal.com", + "SeqApiKey": "CPwiXcmm7Utt0X07Rhdw", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ], + "RepoManagerAccountUrl": "https://qa-repomanage.lcu-internal.com/#/Account/", //Refactor_TODO: move to a better model + "ProgrammingRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "RepoManagerAdminRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "FloorPriceAcceptedRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "CollectionsClerkRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "CreditDebitsRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "CollectionsRecipients": [ //Refactor_TODO: move to a better model + "ItDevelopment@landmarkcu.onmicrosoft.com" + ], + "OrganizationCode": "", //Refactor_TODO: Whats the point? + "TransactionTypeCodes": [ //Refactor_TODO: move to a better model + "CHAS", + "CHST", + "COBR", + "COFR", + "EXT", + "FLCO", + "FLCR", + "LCAP", + "LRCT", + "LTCG", + "LTCW", + "PDSB", + "POFF", + "PRCT", + "RPMT", + "SPMT", + "XPMT" + ], + "TransactionTypeCodeQueryPrefix": "&transactionTypeCodes=", //Refactor_TODO: move to a better model + "TransactionHistoryParameterDateFormat": "MM-dd-yyyy", //Refactor_TODO: move to a better model + "TransactionHistoryStartDateOffset": -120, //Refactor_TODO: move to a better model + "TransactionsQueryTimeout": 300, + "SettingsCacheExpirationSeconds": 90, //Refactor_TODO: move to a better model + "FeatureFlags": { + "RunRepoAccountProcessingConsumer": true, + "RunRepoBatchProcessingConsumer": true, + "RunRepoBatchCompletionProcessingConsumer": true + } + }, + "SqlConnectionConfiguration": { + "ConnectionString": "data source=localhost;initial catalog=RepoDB;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Encrypt=False;", + "MaxRetryCount": 3, + "MaxRetryDelay": "00:00:30" + }, + "ConnectionConfiguration": { + "HostUrl": "localhost", + "UserName": "guest", + "Password": "guest" + }, + "RepoAccountProcessingReceiveEndpoint": { + "ReceiveEndpoint": "LCU.Repo.RepoAccountProcessing", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 1 + }, + "RepoBatchProcessingReceiveEndpoint": { + "ReceiveEndpoint": "LCU.Repo.RepoBatchRequestProcessing", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 1 + }, + "RepoBatchCompletionProcessingReceiveEndpoint": { + "ReceiveEndpoint": "LCU.Repo.RepoBatchCompletionProcessing", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 10 + }, + "CollectionsApiConfiguration": { + "Url": "https://qa-collectionsapi.lcu-internal.com", + "RetryCount": 5, + "ByAccountNo": "/api/account/ByAccountNo/", + "GetConfiguration": "/api/Configuration", + "GetAccountHistory": "/api/account/{accountId}/history" + }, + "CoreApiWrapperConfiguration": { + "Url": "https://qa-core-api-wrapper-api.apps.azure.lcu-internal.com", + "RetryCount": 5, + "TransactionTypeCodeQueryPrefix": "&transactionTypeCodes=", + "TransactionHistoryParameterDateFormat": "MM-dd-yyyy", + "TransactionHistoryStartDateOffset": -120, + "GetBranch": "/api/core/branch", + "GetLoanCollateral": "/api/core/Loan/GetLoanCollateral/{loanId}", + "GetLoanDetails": "/api/core/Loan/GetLoanDetails/{loanId}", + "GetTransactionHistory": "/api/core/Transaction/GetTransactionHistory?accountNumber={accountNo}&startDateRange={startDate}&endDateRange={endDate}", + "GetMemberByAccountNumber": "/api/core/members/GetMemberByAccountNumber/{loanId}" + }, + "NotificationApiConfiguration": { + "Url": "https://qa-notificationsapi.lcu-internal.com", + "RetryCount": 5, + "Email": "/api/Email" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/using.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/using.cs new file mode 100644 index 0000000..38a69fb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApi/using.cs @@ -0,0 +1,5 @@ +global using LCU.Library.Internal.Exceptions; +global using Serilog; +global using System; +global using System.Linq; +global using System.Threading.Tasks; diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj new file mode 100644 index 0000000..009ab54 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/LCU.API.Internal.RepoApiTests.csproj @@ -0,0 +1,41 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + Always + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/RepoTestData.json b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/RepoTestData.json new file mode 100644 index 0000000..228a592 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/RepoTestData.json @@ -0,0 +1,106 @@ +{ + "RepoTest": { + "Repo": { + "RepoId": "12351", + "AccountNo": "19620203513", + "MemberNo": "9067950", + "LoanNo": "19620203513", + "AccountTypeCode": "AUI", + "Branch": "0", + "Collector": "Bryan Schuyler", + "Status": "04R", + "Balance": "36214.90", + "DueToPayDate": "2025-01-04 00:00:00.000", + "O4RDate": "2025-03-31 16:15:14.077", + "DateFirstDelinquent": "", + "DelinquentAmount": "1800.77", + "DaysDelinquent": "86", + "PaidSkipAmount": "", + "SubStatus": "", + "RequiredPaymentAmount": "1800.77", + "CurrentDelinquentAmount": "1800.77", + "LstPmtAmt": "844.96", + "OrigAmount": "50167.72", + "LastPaymentDate": "2025-01-07 00:00:00.000", + "DateFirstIntellect": "2025-03-31 16:15:14.077", + "PrimaryIndividualId": "557697", + "Bankruptcy": "False", + "ManualRedemptionDate": "", + "RedemptionExpiredDate": "", + "DateClosed": "", + "WrittenOffDate": "", + "ReferenceStatus": "Active", + "ReferenceId": "1298878", + "ReferenceCaseId": "24433", + "AccountLastProcessed": "2025-03-31 15:48:46.370", + "OrigAccountTypeCode": "AUI", + "SixtyPlusCollectorId": "" + }, + "Collateral": { + "CollateralId": 1, + "RepoId": 1 + }, + "TemenosCaseFailure": { + "CaseId": 10, + "CaseNumber": "123456789", + "CaseDescription": "Test Case", + "CaseStageId": "1", + "RepoStatus": "10", + "CaseStatusId": "1", + "CaseTitle": "Test Case Title", + "CaseTypeBasisId": "1", + "CaseTypeId": "1", + "CreatedUserId": "1", + "TitleOrdered": "false", + "RepoTypeId": "1", + "RepoAgentId": "1" + }, + "TemenosCaseSuccess": { + "CaseId": 10, + "CaseNumber": "123456789", + "CaseDescription": "Test Case", + "CaseStageId": "1", + "RepoStatus": "100", + "CaseStatusId": "1", + "CaseTitle": "Test Case Title", + "CaseTypeBasisId": "1", + "CaseTypeId": "7", + "CreatedUserId": "1", + "TitleOrdered": "false", + "RepoTypeId": "1", + "RepoAgentId": "1" + }, + "TemenosAccount": { + "CustomFieldMap": [ + { + "User Status Code 1": "True" + } + ] + }, + "Loan": { + "MemberId": "9067950", + "LoanId": "12351", + "ExternalLoanId": "12351", + "LoanType": "AUI" + }, + "Letters": { + "NoticeType": "Repo" + }, + "Transaction": { + "runningBalance": 20000.00, + "postingDate": "2025-01-07 00:00:00.000", + "amount": 1000.00, + "transactionTypeCode": "TRC", + "transactionTypeDescription": "Test Transaction" + }, + "Member": { + "MemberId": "9067950", + "MemberName": "Timmy Tester" + }, + "AuctionAssignment": { + "Id": 1, + "AuctionId": "12345" + }, + "Settings": {} + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/EventAlertServiceTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/EventAlertServiceTests.cs new file mode 100644 index 0000000..ccb69ca --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/EventAlertServiceTests.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using Moq; +using Xunit; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class EventAlertServiceTests + { + private readonly Mock _eventAlertRepositoryMock; + private readonly Mock _unitOfWorkMock; + private readonly Mock _accountRepositoryMock; + private readonly EventAlertService _service; + + public EventAlertServiceTests() + { + _eventAlertRepositoryMock = new Mock(); + _unitOfWorkMock = new Mock(); + _accountRepositoryMock = new Mock(); + _service = new EventAlertService( + _eventAlertRepositoryMock.Object, + _unitOfWorkMock.Object, + _accountRepositoryMock.Object); + } + + [Fact] + public async Task ListAll_ShouldReturnAllEventAlerts() + { + // Arrange + var eventAlerts = new List { new EventAlert { Id = 1 }, new EventAlert { Id = 2 } }; + _eventAlertRepositoryMock.Setup(repo => repo.ListAllAsync()).ReturnsAsync(eventAlerts); + + // Act + var result = await _service.ListAll(); + + // Assert + Assert.Equal(eventAlerts, result); + } + + [Fact] + public async Task ListAll_ShouldThrowServerErrorException_OnException() + { + // Arrange + _eventAlertRepositoryMock.Setup(repo => repo.ListAllAsync()).ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListAll()); + } + + [Fact] + public async Task ListUnread_ShouldReturnUnreadEventAlerts() + { + // Arrange + var unreadAlerts = new List { new EventAlert { Id = 1 } }; + _eventAlertRepositoryMock.Setup(repo => repo.ListUnreadAsync()).ReturnsAsync(unreadAlerts); + + // Act + var result = await _service.ListUnread(); + + // Assert + Assert.Equal(unreadAlerts, result); + } + + [Fact] + public async Task ListUnread_ShouldThrowServerErrorException_OnException() + { + // Arrange + _eventAlertRepositoryMock.Setup(repo => repo.ListUnreadAsync()).ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListUnread()); + } + + [Fact] + public async Task ListWithinDateRange_ShouldReturnAlertsWithinDateRange() + { + // Arrange + var start = DateTime.Now.AddDays(-1); + var end = DateTime.Now; + var alerts = new List { new EventAlert { Id = 1 } }; + _eventAlertRepositoryMock.Setup(repo => repo.ListWithinDateRangeAsync(start, end)).ReturnsAsync(alerts); + + // Act + var result = await _service.ListWithinDateRange(start, end); + + // Assert + Assert.Equal(alerts, result); + } + + [Fact] + public async Task ListWithinDateRange_ShouldThrowServerErrorException_OnException() + { + // Arrange + var start = DateTime.Now.AddDays(-1); + var end = DateTime.Now; + _eventAlertRepositoryMock.Setup(repo => repo.ListWithinDateRangeAsync(start, end)).ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListWithinDateRange(start, end)); + } + + [Fact] + public async Task SaveAsync_ShouldSaveEventAlert() + { + // Arrange + var eventAlert = new EventAlert { RepoId = 1, AccountNo = "123" }; + _accountRepositoryMock.Setup(repo => repo.FindByIdAsync(eventAlert.RepoId.Value)) + .ReturnsAsync(new RepoAccount { Status = "ACTIVE" }); + + // Act + var result = await _service.SaveAsync(eventAlert); + + // Assert + _eventAlertRepositoryMock.Verify(repo => repo.AddAsync(eventAlert), Times.Once); + _unitOfWorkMock.Verify(uow => uow.CompleteAsync(), Times.Once); + Assert.Equal(eventAlert, result); + } + + [Fact] + public async Task SaveAsync_ShouldThrowUnwantedStatusException_WhenStatusIsCancelledOrRedeemed() + { + // Arrange + var eventAlert = new EventAlert { RepoId = 1, AccountNo = "123" }; + _accountRepositoryMock.Setup(repo => repo.FindByIdAsync(eventAlert.RepoId.Value)) + .ReturnsAsync(new RepoAccount { Status = "REDEEMED" }); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.SaveAsync(eventAlert)); + } + + [Fact] + public async Task SaveAsync_ShouldThrowServerErrorException_OnException() + { + // Arrange + var eventAlert = new EventAlert { RepoId = 1, AccountNo = "123" }; + _accountRepositoryMock.Setup(repo => repo.FindByIdAsync(eventAlert.RepoId.Value)) + .ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.SaveAsync(eventAlert)); + } + + [Fact] + public async Task DeleteAsync_ShouldDeleteEventAlert() + { + // Arrange + var eventAlert = new EventAlert { Id = 1 }; + _eventAlertRepositoryMock.Setup(repo => repo.FindByIdAsync(eventAlert.Id)).ReturnsAsync(eventAlert); + + // Act + var result = await _service.DeleteAsync(eventAlert.Id); + + // Assert + _eventAlertRepositoryMock.Verify(repo => repo.Remove(eventAlert), Times.Once); + _unitOfWorkMock.Verify(uow => uow.CompleteAsync(), Times.Once); + Assert.Equal(eventAlert, result); + } + + [Fact] + public async Task DeleteAsync_ShouldThrowNotFoundException_WhenEventAlertNotFound() + { + // Arrange + _eventAlertRepositoryMock.Setup(repo => repo.FindByIdAsync(It.IsAny())).ReturnsAsync((EventAlert)null); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.DeleteAsync(1)); + } + + [Fact] + public async Task DeleteAsync_ShouldThrowServerErrorException_OnException() + { + // Arrange + _eventAlertRepositoryMock.Setup(repo => repo.FindByIdAsync(It.IsAny())).ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.DeleteAsync(1)); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountProcessorTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountProcessorTests.cs new file mode 100644 index 0000000..84142a7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountProcessorTests.cs @@ -0,0 +1,271 @@ +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Producer; +using LCU.API.Internal.RepoApi.Old_Repo_Services.Producer; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Consumer; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using System.IO; +using System.Reflection; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class RepoAccountProcessorTests + { + private readonly Mock _mockRepoAccountProcessorService; + private readonly Mock _mockAccountAlertCheckService; + private readonly Mock _mockEmailer; + private readonly Mock _mockSettingsService; + private readonly Mock _mockCollectionsDao; + private readonly Mock _mockCoreDao; + private readonly Mock _mockRepoAccountService; + private readonly Mock _mockRepoLetterService; + private readonly Mock _mockAuctionAssignmentService; + private readonly RepoAccountProcessor _processor; + private readonly JObject _mockJObject; + + + public RepoAccountProcessorTests() + { + _mockRepoAccountProcessorService = new Mock(); + _mockAccountAlertCheckService = new Mock(); + _mockEmailer = new Mock(); + _mockSettingsService = new Mock(); + _mockCollectionsDao = new Mock(); + _mockCoreDao = new Mock(); + _mockRepoAccountService = new Mock(); + _mockRepoLetterService = new Mock(); + _mockAuctionAssignmentService = new Mock(); + + _processor = new RepoAccountProcessor( + _mockRepoAccountProcessorService.Object, + _mockAccountAlertCheckService.Object, + _mockCollectionsDao.Object, + _mockEmailer.Object, + _mockSettingsService.Object, + _mockRepoAccountService.Object, + _mockRepoLetterService.Object, + _mockCoreDao.Object, + _mockAuctionAssignmentService.Object + ); + + JsonSerializer se = new JsonSerializer(); + + string testFile = "./RepoTestData.json"; + + using (StreamReader r = new StreamReader(testFile)) + { + string json = r.ReadToEnd(); + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + _mockJObject = JObject.Load(reader); + } + } + + [Fact] + public async Task HandleAccount_ShouldReturnAutoclosed() + { + var accountNumber = _mockJObject["RepoTest"]["Repo"]["AccountNo"].ToString(); + var item = new ProcessingItem { AccountNo = accountNumber }; + var processingSlip = new RepoAccountProcessingSlip + { + AccountNo = accountNumber, + RecoveryAccount = null, + ExistingRepoAccount = _mockJObject["RepoTest"]["Repo"].ToObject() + }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(processingSlip.AccountNo)).ReturnsAsync(processingSlip.ExistingRepoAccount); + + var result = await _processor.HandleAccount(item); + + Assert.Equal(RepoProcessingResult.Autoclosed, result); + } + + [Fact] + public async Task HandleAccount_IsUnconvertedIntellectAccount() + { + var accountNumber = _mockJObject["RepoTest"]["Repo"]["AccountNo"].ToString(); + var item = new ProcessingItem { AccountNo = accountNumber }; + + TemenosAccount recoveryAccount = new TemenosAccount() {Cases = new List { _mockJObject["RepoTest"]["TemenosCaseSuccess"].ToObject() } }; + + recoveryAccount.CustomFieldMap = new Dictionary { { "User Status Code 1", "04R" } }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(accountNumber)).ReturnsAsync(_mockJObject["RepoTest"]["Repo"].ToObject()); + _mockCollectionsDao.Setup(x => x.RetrieveAccountAsync(accountNumber)).ReturnsAsync(recoveryAccount); + _mockSettingsService.Setup(x => x.GetCachedRepoSettingsAsync()).ReturnsAsync(_mockJObject["RepoTest"]["Settings"].ToObject()); + + var result = await _processor.HandleAccount(item); + + Assert.Equal(RepoProcessingResult.Unconverted, result); + } + + [Fact] + public void VerifyAccountNo_ShouldThrowException_WhenItemIsNull() + { + // Act & Assert + Assert.Throws(() => _processor.VerifyAccountNo(null)); + } + + [Fact] + public async Task CreateProcessingSlip_ShouldReturnSlip_WithValidData() + { + // Arrange + var item = new ProcessingItem { AccountNo = "12345", CaseId = "1" }; + var recoveryAccount = new TemenosAccount(); + var loan = new LoanDetail(); + + _mockCollectionsDao + .Setup(x => x.RetrieveAccountAsync("12345")) + .ReturnsAsync(recoveryAccount); + + _mockCoreDao + .Setup(x => x.RetrieveLoanAsync("12345")) + .ReturnsAsync(loan); + + // Act + var result = await _processor.CreateProcessingSlip(item); + + // Assert + Assert.NotNull(result); + Assert.Equal("12345", result.AccountNo); + Assert.Equal(recoveryAccount, result.RecoveryAccount); + Assert.Equal(loan, result.Loan); + } + + [Fact] + public async Task HandleAccount_ShouldReturnIneligible_WhenNotPossiblyRepossessionCase() + { + // Arrange + var accountNumber = _mockJObject["RepoTest"]["Repo"]["AccountNo"].ToString(); + var item = new ProcessingItem { AccountNo = accountNumber, CaseId = "10" }; + + TemenosAccount recoveryAccount = new TemenosAccount() { Cases = new List { _mockJObject["RepoTest"]["TemenosCaseFailure"].ToObject() } }; + + RepoAccount existingAccount = _mockJObject["RepoTest"]["Repo"].ToObject(); + existingAccount.Collaterals = new List + { + _mockJObject["RepoTest"]["Collateral"].ToObject() + }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(accountNumber)).ReturnsAsync(existingAccount); + _mockRepoAccountProcessorService.Setup(x => x.UpdateRepoAccountFromParts(It.IsAny())) + .Returns(_mockJObject["RepoTest"]["Repo"].ToObject()); + _mockCollectionsDao.Setup(x => x.RetrieveAccountAsync(accountNumber)).ReturnsAsync(recoveryAccount); + _mockCoreDao.Setup(x => x.RetrieveLoanAsync(accountNumber)).ReturnsAsync(_mockJObject["RepoTest"]["Loan"].ToObject()); + _mockRepoLetterService.Setup(x => x.ListByAccountIdAsync(recoveryAccount.AccountId)).ReturnsAsync(new List()); + _mockRepoAccountProcessorService.Setup(x => x.CreateRepoAccount(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new RepoAccount()); + _mockSettingsService.Setup(x => x.GetCachedRepoSettingsAsync()).ReturnsAsync(_mockJObject["RepoTest"]["Settings"].ToObject()); + + // Act + var result = await _processor.HandleAccount(item); + + // Assert + Assert.Equal(RepoProcessingResult.Ineligible, result); + } + + [Fact] + public async Task HandleAccount_ShouldThrowException_WhenExistingRepoAccountHasNoCollateral() + { + // Arrange + var accountNumber = "12345"; + var item = new ProcessingItem { AccountNo = accountNumber }; + // var existingRepoAccount = new RepoAccount { Collateral = null }; + var processingSlip = new RepoAccountProcessingSlip + { + AccountNo = accountNumber, + RecoveryAccount = new TemenosAccount(), + ExistingRepoAccount = _mockJObject["RepoTest"]["Repo"].ToObject() + }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(accountNumber)).ReturnsAsync(_mockJObject["RepoTest"]["Repo"].ToObject()); + _mockCollectionsDao.Setup(x => x.RetrieveAccountAsync(accountNumber)).ReturnsAsync(processingSlip.RecoveryAccount); + _mockSettingsService.Setup(x => x.GetCachedRepoSettingsAsync()).ReturnsAsync(new Settings()); + + // Act & Assert + await Assert.ThrowsAsync(() => _processor.HandleAccount(item)); + } + + [Fact] + public async Task HandleAccount_ShouldReturnUpdated_WhenExistingAccountIsUpdated() + { + // Arrange + var accountNumber = _mockJObject["RepoTest"]["Repo"]["AccountNo"].ToString(); + var item = new ProcessingItem { AccountNo = accountNumber, CaseId = "10" }; + + TemenosAccount recoveryAccount = new TemenosAccount() { Cases = new List { _mockJObject["RepoTest"]["TemenosCaseSuccess"].ToObject() } }; + + RepoAccount existingAccount = _mockJObject["RepoTest"]["Repo"].ToObject(); + existingAccount.Collaterals = new List + { + _mockJObject["RepoTest"]["Collateral"].ToObject() + }; + + List transactions = new List + { + _mockJObject["RepoTest"]["Transaction"].ToObject() + }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(accountNumber)).ReturnsAsync(existingAccount); + _mockRepoAccountProcessorService.Setup(x => x.UpdateRepoAccountFromParts(It.IsAny())) + .Returns(_mockJObject["RepoTest"]["Repo"].ToObject()); + _mockCollectionsDao.Setup(x => x.RetrieveAccountAsync(accountNumber)).ReturnsAsync(recoveryAccount); + _mockSettingsService.Setup(x => x.GetCachedRepoSettingsAsync()).ReturnsAsync(_mockJObject["RepoTest"]["Settings"].ToObject()); + _mockCoreDao.Setup(x => x.RetrieveLoanAsync(accountNumber)).ReturnsAsync(_mockJObject["RepoTest"]["Loan"].ToObject()); + _mockCoreDao.Setup(x => x.RetrieveTransactionsAsync(accountNumber)).ReturnsAsync(transactions); + _mockCoreDao.Setup(x => x.RetrieveMemberAsync(accountNumber)).ReturnsAsync(_mockJObject["RepoTest"]["Member"].ToObject()); + _mockRepoLetterService.Setup(x => x.ListByAccountIdAsync(recoveryAccount.AccountId)).ReturnsAsync(new List()); + _mockAuctionAssignmentService.Setup(x => x.GetListByIdsAsync(It.IsAny>())) + .ReturnsAsync(new List { _mockJObject["RepoTest"]["AuctionAssignment"].ToObject() }); + _mockRepoAccountProcessorService.Setup(x => x.UpdateRepoAccountFromParts(It.IsAny())) + .Returns(_mockJObject["RepoTest"]["Repo"].ToObject()); + + // Act + var result = await _processor.HandleAccount(item); + + // Assert + Assert.Equal(RepoProcessingResult.Updated, result); + } + + [Fact] + public async Task HandleAccount_ShouldThrowException_WhenProcessingItemIsNull() + { + // Act & Assert + await Assert.ThrowsAsync(() => _processor.HandleAccount(null)); + } + + [Fact] + public async Task HandleAccount_ShouldReturnUnconverted_WhenRecoveryAccountHasOldIntellectStatus() + { + // Arrange + var accountNumber = "12345"; + var item = new ProcessingItem { AccountNo = accountNumber }; + var recoveryAccount = new TemenosAccount + { + CustomFieldMap = new Dictionary { { RepoAccountProcessor.UserStatusFieldOld, "OldStatus" } } + }; + var existingRepoAccount = new RepoAccount { Status = "OldStatus" }; + var processingSlip = new RepoAccountProcessingSlip + { + AccountNo = accountNumber, + RecoveryAccount = recoveryAccount, + ExistingRepoAccount = existingRepoAccount + }; + + _mockRepoAccountService.Setup(x => x.GetNewestOpenAccount(accountNumber)).ReturnsAsync(existingRepoAccount); + _mockCollectionsDao.Setup(x => x.RetrieveAccountAsync(accountNumber)).ReturnsAsync(recoveryAccount); + _mockSettingsService.Setup(x => x.GetCachedRepoSettingsAsync()).ReturnsAsync(new Settings()); + + // Act + var result = await _processor.HandleAccount(item); + + // Assert + Assert.Equal(RepoProcessingResult.Unconverted, result); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountServiceTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountServiceTests.cs new file mode 100644 index 0000000..4b5711c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoAccountServiceTests.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Repos.Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Models.Results; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Filters; +using LCU.API.Internal.RepoApi.Models.Old_Repo_Models.Requests; +using Moq; +using Xunit; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class RepoAccountServiceTests + { + private readonly Mock _repoAccountRepositoryMock; + private readonly Mock _repoStatusChangeRepositoryMock; + private readonly Mock _repoManualStatusRepositoryMock; + private readonly Mock _collateralRepositoryMock; + private readonly Mock _unitOfWorkMock; + private readonly RepoAccountService _service; + + public RepoAccountServiceTests() + { + _repoAccountRepositoryMock = new Mock(); + _repoStatusChangeRepositoryMock = new Mock(); + _repoManualStatusRepositoryMock = new Mock(); + _collateralRepositoryMock = new Mock(); + _unitOfWorkMock = new Mock(); + + _service = new RepoAccountService( + _repoAccountRepositoryMock.Object, + _repoStatusChangeRepositoryMock.Object, + _repoManualStatusRepositoryMock.Object, + _collateralRepositoryMock.Object, + _unitOfWorkMock.Object); + } + + [Fact] + public async Task ListAll_ShouldReturnPagedResult_WhenCalled() + { + // Arrange + var pageFilter = new PageFilter { Page = 1, Limit = 10 }; + var repoAccountFilter = new RepoAccountPostFilter { RepoIdList = new List { 1, 2 }, Status = "Open" }; + var expectedResult = new PagedResult(); + + _repoAccountRepositoryMock + .Setup(repo => repo.ListAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(expectedResult); + + // Act + var result = await _service.ListAll(pageFilter, repoAccountFilter); + + // Assert + Assert.Equal(expectedResult, result); + } + + [Fact] + public async Task ListAll_ShouldThrowServerErrorException_WhenExceptionOccurs() + { + // Arrange + var pageFilter = new PageFilter { Page = 1, Limit = 10 }; + var repoAccountFilter = new RepoAccountPostFilter(); + + _repoAccountRepositoryMock + .Setup(repo => repo.ListAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListAll(pageFilter, repoAccountFilter)); + } + + [Fact] + public async Task FindByAuctionAssignmentId_ShouldReturnRepoAccount_WhenFound() + { + // Arrange + var auctionAssignmentId = 1; + var expectedRepoAccount = new RepoAccount(); + + _repoAccountRepositoryMock + .Setup(repo => repo.FindByAuctionAssignmentIdAsync(auctionAssignmentId)) + .ReturnsAsync(expectedRepoAccount); + + // Act + var result = await _service.FindByAuctionAssignmentId(auctionAssignmentId); + + // Assert + Assert.Equal(expectedRepoAccount, result); + } + + [Fact] + public async Task FindByAuctionAssignmentId_ShouldThrowServerErrorException_WhenExceptionOccurs() + { + // Arrange + var auctionAssignmentId = 1; + + _repoAccountRepositoryMock + .Setup(repo => repo.FindByAuctionAssignmentIdAsync(auctionAssignmentId)) + .ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.FindByAuctionAssignmentId(auctionAssignmentId)); + } + + [Fact] + public async Task SaveAsync_ShouldSaveRepoAccountAndAddStatusChange() + { + // Arrange + var repoAccount = new RepoAccount { RepoId = 1, Status = "Open" }; + var savedRepoAccount = new RepoAccount { RepoId = 1, Status = "Open" }; + + _repoAccountRepositoryMock + .Setup(repo => repo.AddAsync(repoAccount)) + .ReturnsAsync(savedRepoAccount); + + // Act + var result = await _service.SaveAsync(repoAccount); + + // Assert + Assert.Equal(repoAccount, result); + _unitOfWorkMock.Verify(uow => uow.CompleteAsync(), Times.Once); + } + + [Fact] + public async Task SaveAsync_ShouldThrowServerErrorException_WhenExceptionOccurs() + { + // Arrange + var repoAccount = new RepoAccount { RepoId = 1, Status = "Open" }; + + _repoAccountRepositoryMock + .Setup(repo => repo.AddAsync(repoAccount)) + .ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.SaveAsync(repoAccount)); + } + + [Fact] + public async Task Get_ShouldReturnRepoAccount_WhenFound() + { + // Arrange + var id = 1; + var expectedRepoAccount = new RepoAccount(); + + _repoAccountRepositoryMock + .Setup(repo => repo.FindByIdAsync(id)) + .ReturnsAsync(expectedRepoAccount); + + // Act + var result = await _service.Get(id); + + // Assert + Assert.Equal(expectedRepoAccount, result); + } + + [Fact] + public async Task Get_ShouldThrowNotFoundException_WhenRepoAccountNotFound() + { + // Arrange + var id = 1; + + _repoAccountRepositoryMock + .Setup(repo => repo.FindByIdAsync(id)) + .ReturnsAsync((RepoAccount)null); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.Get(id)); + } + + [Fact] + public async Task Get_ShouldThrowServerErrorException_WhenExceptionOccurs() + { + // Arrange + var id = 1; + + _repoAccountRepositoryMock + .Setup(repo => repo.FindByIdAsync(id)) + .ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.Get(id)); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoCompanySelectionServiceTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoCompanySelectionServiceTests.cs new file mode 100644 index 0000000..48d1455 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoCompanySelectionServiceTests.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using Moq; +using Xunit; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class RepoCompanySelectionServiceTests + { + private readonly Mock _mockRepoCompanySelectionRepository; + private readonly Mock _mockUnitOfWork; + private readonly RepoCompanySelectionService _service; + + public RepoCompanySelectionServiceTests() + { + _mockRepoCompanySelectionRepository = new Mock(); + _mockUnitOfWork = new Mock(); + _service = new RepoCompanySelectionService(_mockRepoCompanySelectionRepository.Object, _mockUnitOfWork.Object); + } + + [Fact] + public async Task ListAllAsync_ShouldReturnAllRepoCompanySelections() + { + // Arrange + var mockData = new List + { + new RepoCompanySelection { Id = 1}, + new RepoCompanySelection { Id = 2} + }; + _mockRepoCompanySelectionRepository.Setup(repo => repo.ListAllAsync()).ReturnsAsync(mockData); + + // Act + var result = await _service.ListAllAsync(); + + // Assert + Assert.Equal(mockData, result); + _mockRepoCompanySelectionRepository.Verify(repo => repo.ListAllAsync(), Times.Once); + } + + [Fact] + public async Task ListAllAsync_ShouldThrowServerErrorException_OnException() + { + // Arrange + _mockRepoCompanySelectionRepository.Setup(repo => repo.ListAllAsync()).ThrowsAsync(new Exception()); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListAllAsync()); + } + + [Fact] + public async Task GetAsync_ShouldReturnRepoCompanySelection_WhenFound() + { + // Arrange + var mockData = new RepoCompanySelection { Id = 1}; + _mockRepoCompanySelectionRepository.Setup(repo => repo.FindByIdAsync(1)).ReturnsAsync(mockData); + + // Act + var result = await _service.GetAsync(1); + + // Assert + Assert.Equal(mockData, result); + _mockRepoCompanySelectionRepository.Verify(repo => repo.FindByIdAsync(1), Times.Once); + } + + [Fact] + public async Task GetAsync_ShouldThrowNotFoundException_WhenNotFound() + { + // Arrange + _mockRepoCompanySelectionRepository.Setup(repo => repo.FindByIdAsync(1)).ReturnsAsync((RepoCompanySelection)null); + + // Act & Assert + var exception = await Assert.ThrowsAsync(() => _service.GetAsync(1)); + Assert.Equal("Repo Company Selection with id 1 not found", exception.Message); + } + + [Fact] + public async Task GetAsync_ShouldThrowNotFoundException_OnRepositoryException() + { + // Arrange + _mockRepoCompanySelectionRepository.Setup(repo => repo.FindByIdAsync(1)).ThrowsAsync(new Exception()); + + // Act & Assert + var exception = await Assert.ThrowsAsync(() => _service.GetAsync(1)); + Assert.Equal("Repo Company Selection with id 1 not found", exception.Message); + } + + [Fact] + public async Task SaveChangesAsync_ShouldSaveRepoCompanySelections() + { + // Arrange + var mockData = new List + { + new RepoCompanySelection { Id = 1}, + new RepoCompanySelection { Id = 2} + }; + + // Act + await _service.SaveChangesAsync(mockData); + + // Assert + _mockRepoCompanySelectionRepository.Verify(repo => repo.SaveRange(mockData), Times.Once); + _mockUnitOfWork.Verify(uow => uow.CompleteAsync(), Times.Once); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoLetterServiceTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoLetterServiceTests.cs new file mode 100644 index 0000000..29d97a8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/RepoLetterServiceTests.cs @@ -0,0 +1,121 @@ +using LCU.API.Collections.CollectionsApiLib.Models; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Old_Repo_Exceptions; +using LCU.API.Internal.RepoApi.Repos.Models; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Old_Repo_Services; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class RepoLetterServiceTests + { + private readonly Mock _mockLetterRepository; + private readonly Mock _mockCollectionsDao; + private readonly RepoLetterService _service; + + public RepoLetterServiceTests() + { + _mockLetterRepository = new Mock(); + _mockCollectionsDao = new Mock(); + _service = new RepoLetterService(_mockLetterRepository.Object, _mockCollectionsDao.Object); + } + + [Fact] + public async Task ListByAccountIdAsync_ReturnsEmptyList_WhenNoCollectionsLetters() + { + // Arrange + int accountId = 1; + _mockCollectionsDao + .Setup(x => x.RetrieveHistoryAsync(accountId)) + .ReturnsAsync(new List()); + + // Act + var result = await _service.ListByAccountIdAsync(accountId); + + // Assert + Assert.Empty(result); + } + + [Fact] + public async Task ListByAccountIdAsync_ReturnsProcessedLetters_WhenCollectionsLettersExist() + { + // Arrange + int accountId = 1; + var collectionsHistory = new List + { + new TemenosHistory { Type = HistoryType.Letter, Message = "Test Message", Created = DateTime.UtcNow } + }; + var letters = new List + { + new Letter { LetterDescription = "Test", DaysToWait = 5, ActionCode = "A1" } + }; + + _mockCollectionsDao + .Setup(x => x.RetrieveHistoryAsync(accountId)) + .ReturnsAsync(collectionsHistory); + + _mockLetterRepository + .Setup(x => x.ListAsync()) + .ReturnsAsync(letters); + + // Act + var result = await _service.ListByAccountIdAsync(accountId); + + // Assert + Assert.Single(result); + Assert.Equal("Test Message", result.First().NoticeType); + } + + [Fact] + public async Task ListByAccountIdAsync_ThrowsServerErrorException_OnGeneralException() + { + // Arrange + int accountId = 1; + _mockCollectionsDao + .Setup(x => x.RetrieveHistoryAsync(accountId)) + .ThrowsAsync(new Exception("Test Exception")); + + // Act & Assert + await Assert.ThrowsAsync(() => _service.ListByAccountIdAsync(accountId)); + } + + [Fact] + public void ProcessLetters_ThrowsMissingRequirementException_WhenLetterDescriptionIsNull() + { + // Arrange + var collectionsLetters = new List + { + new TemenosHistory { Message = "Test Message", Created = DateTime.UtcNow } + }; + var letters = new List + { + new Letter { LetterDescription = null } + }; + + // Act & Assert + Assert.Throws(() => + RepoLetterService.ProcessLetters(collectionsLetters, letters)); + } + + [Fact] + public void ProcessLetters_ReturnsProcessedLetters_WhenValidDataProvided() + { + // Arrange + var collectionsLetters = new List + { + new TemenosHistory { Message = "Test Message", Created = DateTime.UtcNow } + }; + var letters = new List + { + new Letter { LetterDescription = "Test", DaysToWait = 5, ActionCode = "A1" } + }; + + // Act + var result = RepoLetterService.ProcessLetters(collectionsLetters, letters); + + // Assert + Assert.Single(result); + Assert.Equal("Test Message", result.First().NoticeType); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/SettingsServiceTests.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/SettingsServiceTests.cs new file mode 100644 index 0000000..12e187e --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/ServiceTests/SettingsServiceTests.cs @@ -0,0 +1,153 @@ +using Microsoft.Extensions.Caching.Memory; +using LCU.API.Internal.RepoApi.Old_Repo_Services; +using LCU.API.Internal.RepoApi.Repos.Repositories; +using LCU.API.Internal.RepoApi.Repos.Models.Settings; +using LCU.API.Internal.RepoApi.Configurations; +using LCU.API.Internal.RepoApi.Old_Repo_DataAccess; +using LCU.API.Internal.RepoApi.Repos.Repositories.Settings; + +namespace LCU.API.Internal.RepoApiTests.ServiceTests +{ + public class SettingsServiceTests + { + private readonly Mock _mockAccountTypeRepository; + private readonly Mock _mockAllowedStatusChangeRepository; + private readonly Mock _mockBodyStyleRepository; + private readonly Mock _mockCleaningLevelRepository; + private readonly Mock _mockCollateralTypeRepository; + private readonly Mock _mockCollateralTypeToVehicleTypeRepository; + private readonly Mock _mockHoldStatusRepository; + private readonly Mock _mockLoanCategoryRepository; + private readonly Mock _mockLocationTypeRepository; + private readonly Mock _mockVehicleTypeRepository; + private readonly Mock _mockExternalUserRepository; + private readonly Mock _mockRepoCompanyRepository; + private readonly Mock _mockUnitOfWork; + private readonly Mock _mockMemoryCache; + private readonly Mock _mockCollectionsDao; + private readonly ApiConfiguration _apiConfiguration; + private readonly SettingsService _settingsService; + + public SettingsServiceTests() + { + _mockAccountTypeRepository = new Mock(); + _mockAllowedStatusChangeRepository = new Mock(); + _mockBodyStyleRepository = new Mock(); + _mockCleaningLevelRepository = new Mock(); + _mockCollateralTypeRepository = new Mock(); + _mockCollateralTypeToVehicleTypeRepository = new Mock(); + _mockHoldStatusRepository = new Mock(); + _mockLoanCategoryRepository = new Mock(); + _mockLocationTypeRepository = new Mock(); + _mockVehicleTypeRepository = new Mock(); + _mockExternalUserRepository = new Mock(); + _mockRepoCompanyRepository = new Mock(); + _mockUnitOfWork = new Mock(); + _mockMemoryCache = new Mock(); + _mockCollectionsDao = new Mock(); + _apiConfiguration = new ApiConfiguration + { + SettingsCacheExpirationSeconds = 60, + TransactionTypeCodes = new List { "Code1", "Code2" }, + TransactionTypeCodeQueryPrefix = "Prefix", + TransactionHistoryParameterDateFormat = "yyyy-MM-dd", + TransactionHistoryStartDateOffset = 30 + }; + + _settingsService = new SettingsService( + _mockAccountTypeRepository.Object, + _mockAllowedStatusChangeRepository.Object, + _mockBodyStyleRepository.Object, + _mockCleaningLevelRepository.Object, + _mockCollateralTypeRepository.Object, + _mockCollateralTypeToVehicleTypeRepository.Object, + _mockHoldStatusRepository.Object, + _mockLoanCategoryRepository.Object, + _mockLocationTypeRepository.Object, + _mockVehicleTypeRepository.Object, + _mockExternalUserRepository.Object, + _mockRepoCompanyRepository.Object, + _mockUnitOfWork.Object, + _mockMemoryCache.Object, + _mockCollectionsDao.Object, + _apiConfiguration + ); + } + + [Fact] + public async Task GetCachedRepoSettingsAsync_ShouldReturnSettings_WhenCacheIsEmpty() + { + // Arrange + var cacheEntryMock = new Mock(); + _mockMemoryCache + .Setup(mc => mc.CreateEntry(It.IsAny())) + .Returns(cacheEntryMock.Object); + + _mockAccountTypeRepository.Setup(repo => repo.ListAllAsync()) + .ReturnsAsync(new List { new AccountType { ID = 1, AccountTypeCode = "AT1" } }); + + // Act + var result = await _settingsService.GetCachedRepoSettingsAsync(); + + // Assert + Assert.NotNull(result); + Assert.NotEmpty(result.AccountTypes); + } + + [Fact] + public async Task SaveAsync_ShouldSaveAccountType() + { + // Arrange + var accountType = new AccountType { ID = 1, AccountTypeCode = "AT1" }; + + // Act + var result = await _settingsService.SaveAsync(accountType); + + // Assert + _mockAccountTypeRepository.Verify(repo => repo.AddAsync(accountType), Times.Once); + _mockUnitOfWork.Verify(uow => uow.CompleteAsync(), Times.Once); + Assert.Equal(accountType, result); + } + + [Fact] + public async Task SaveAsync_ShouldThrowException_WhenRepositoryFails() + { + // Arrange + var accountType = new AccountType { ID = 1, AccountTypeCode = "AT1" }; + _mockAccountTypeRepository.Setup(repo => repo.AddAsync(accountType)) + .ThrowsAsync(new Exception("Repository failure")); + + // Act & Assert + await Assert.ThrowsAsync(() => _settingsService.SaveAsync(accountType)); + } + + [Fact] + public async Task UpdateAsync_ShouldUpdateAccountType() + { + // Arrange + var accountType = new AccountType { ID = 1, AccountTypeCode = "AT1", Description = "Updated" }; + _mockAccountTypeRepository.Setup(repo => repo.FindByIdAsync(accountType.ID)) + .ReturnsAsync(new AccountType { ID = 1, AccountTypeCode = "AT1" }); + + // Act + var result = await _settingsService.UpdateAsync(accountType); + + // Assert + _mockAccountTypeRepository.Verify(repo => repo.Update(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(uow => uow.CompleteAsync(), Times.Once); + Assert.Equal(accountType.Description, result.Description); + } + + [Fact] + public async Task UpdateAsync_ShouldThrowNotFoundException_WhenAccountTypeDoesNotExist() + { + // Arrange + var accountType = new AccountType { ID = 1, AccountTypeCode = "AT1" }; + _mockAccountTypeRepository.Setup(repo => repo.FindByIdAsync(accountType.ID)) + .ReturnsAsync((AccountType)null); + + // Act & Assert + await Assert.ThrowsAsync (() => _settingsService.UpdateAsync(accountType)); + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/Usings.cs b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/Usings.cs new file mode 100644 index 0000000..7a8920d --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.API.Internal.RepoApiTests/Usings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using Moq; \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/LCU.Api.Internal.RepoApiSql.sqlproj b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/LCU.Api.Internal.RepoApiSql.sqlproj new file mode 100644 index 0000000..1b688bb --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/LCU.Api.Internal.RepoApiSql.sqlproj @@ -0,0 +1,101 @@ + + + + Debug + AnyCPU + LCU.Api.Internal.RepoApiSql + 2.0 + 4.1 + {c2599576-9529-4af4-9793-8c532a67efc3} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + LCU.Api.Internal.RepoApiSql + LCU.Api.Internal.RepoApiSql + 1033, CI + BySchemaAndSchemaType + True + v4.7.2 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AccountTypeCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AccountTypeCodes.sql new file mode 100644 index 0000000..6a0bd72 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AccountTypeCodes.sql @@ -0,0 +1,9 @@ +CREATE TABLE [dbo].[AccountTypeCodes] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [AccountTypeCode] NVARCHAR (50) NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + [IsVisible] BIT NOT NULL, + [CategoryId] INT NULL, + CONSTRAINT [PK_AccountTypeCodes] PRIMARY KEY CLUSTERED ([ID] ASC), + CONSTRAINT [FK_AccountTypeCodes_LoanCategories] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[LoanCategories] ([ID]) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AllowedStatusChanges.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AllowedStatusChanges.sql new file mode 100644 index 0000000..a5e493b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AllowedStatusChanges.sql @@ -0,0 +1,6 @@ +CREATE TABLE [dbo].[AllowedStatusChanges] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [CurrentStatus] NVARCHAR (100) NOT NULL, + [AllowedChangeStatus] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_AllowedStatusChanges] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentNotes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentNotes.sql new file mode 100644 index 0000000..ef0f7df --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentNotes.sql @@ -0,0 +1,11 @@ +CREATE TABLE [dbo].[AuctionAssignmentNotes] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [AuctionAssignmentId] INT NOT NULL, + [AuctionCustomerNo] NVARCHAR (100) NOT NULL, + [Origin] NVARCHAR (100) NOT NULL, + [Writer] NVARCHAR (100) NULL, + [Type] NVARCHAR (100) NULL, + [Text] NVARCHAR (1024) NULL, + [DateWritten] DATETIME NULL, + CONSTRAINT [PK_AuctionAssignmentNotes] PRIMARY KEY CLUSTERED ([Id] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignments.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignments.sql new file mode 100644 index 0000000..8d7d13a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignments.sql @@ -0,0 +1,33 @@ +CREATE TABLE [dbo].[AuctionAssignments] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [AuctionId] NVARCHAR (50) NOT NULL, + [AuctionNotes] NVARCHAR (1024) NULL, + [AvailablePickupDate] DATETIME NULL, + [LegalSaleDate] DATETIME NULL, + [CleaningLevel] INT NULL, + [SubmissionResponseCode] INT NULL, + [DateSubmitted] DATETIME NULL, + [SubmissionResponseDescription] NVARCHAR (255) NULL, + [SubmissionErrors] NVARCHAR (1024) NULL, + [AuctionCustomerNo] NVARCHAR (100) NULL, + [FloorPrice] DECIMAL (18, 2) NULL, + [SentTitleDocsDate] DATETIME NULL, + [FloorPriceResponseCode] INT NULL, + [FloorPriceResponseDescription] NVARCHAR (255) NULL, + [FloorPriceSubmissionErrors] NVARCHAR (1024) NULL, + [TitleSentResponseCode] INT NULL, + [TitleSentResponseDescription] NVARCHAR (255) NULL, + [TitleSentSubmissionErrors] NVARCHAR (1024) NULL, + [HoldStatus] INT NULL, + [HoldUntil] DATETIME NULL, + [RedeemedDate] DATETIME NULL, + [CollateralVIN] NVARCHAR (50) NULL, + [AddedThroughRepoDatabase] BIT NULL, + [VehicleType] INT NULL, + [BodyStyle] NVARCHAR (50) NULL, + [Inoperable] BIT NULL, + [RetailValue] DECIMAL (18, 2) NULL, + [RepoId] INT NULL, + [IsActive] BIT NOT NULL DEFAULT 1, + CONSTRAINT [PK_CollateralToAuction] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentsToCollaterals.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentsToCollaterals.sql new file mode 100644 index 0000000..58221d4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuctionAssignmentsToCollaterals.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[AuctionAssignmentsToCollaterals] ( + [AuctionAssignmentId] INT NOT NULL, + [CollateralId] INT NOT NULL, + CONSTRAINT [PK_AuctionAssignmentsToCollaterals] PRIMARY KEY CLUSTERED ([AuctionAssignmentId] ASC, [CollateralId] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Auctions.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Auctions.sql new file mode 100644 index 0000000..76019c1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Auctions.sql @@ -0,0 +1,16 @@ +CREATE TABLE [dbo].[Auctions] ( + [AuctionID] NVARCHAR (50) NOT NULL, + [Name] NVARCHAR (255) NULL, + [Active] NVARCHAR (255) NULL, + [Address_Line_1] NVARCHAR (255) NULL, + [Address_Line_2] NVARCHAR (255) NULL, + [City] NVARCHAR (255) NULL, + [State] NVARCHAR (255) NULL, + [ZIP] NVARCHAR (10) NULL, + [Country] NVARCHAR (255) NULL, + [Phone] NVARCHAR (255) NULL, + [Fax] NVARCHAR (255) NULL, + [Latitude] DECIMAL (16, 13) NULL, + [Longitude] DECIMAL (16, 13) NULL, + CONSTRAINT [PK_Auctions] PRIMARY KEY CLUSTERED ([AuctionID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuditTable.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuditTable.sql new file mode 100644 index 0000000..04a9b0f --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/AuditTable.sql @@ -0,0 +1,11 @@ +CREATE TABLE [dbo].[AuditTable] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [ClassName] NVARCHAR (100) NOT NULL, + [Action] NVARCHAR (50) NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + [PerformedBy] NVARCHAR (100) NOT NULL, + [DatePerformed] DATETIME NOT NULL, + [KeyRepresentation] NVARCHAR (100) NOT NULL, + [PropertyValues] NVARCHAR (1024) NULL, + CONSTRAINT [PK_AuditTable] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/BodyStyleCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/BodyStyleCodes.sql new file mode 100644 index 0000000..1e0d6f1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/BodyStyleCodes.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[BodyStyleCodes] ( + [Code] NVARCHAR (50) NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_BodyStyleCodes] PRIMARY KEY CLUSTERED ([Code] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Borrowers.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Borrowers.sql new file mode 100644 index 0000000..219c730 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Borrowers.sql @@ -0,0 +1,7 @@ +CREATE TABLE [dbo].[Borrowers] ( + [RepoId] INT NOT NULL, + [IndividualId] NVARCHAR (50) NOT NULL, + [OwnershipType] INT NULL, + [IndividualName] NVARCHAR (100) NOT NULL, + PRIMARY KEY CLUSTERED ([RepoId] ASC, [IndividualId] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CleaningLevelCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CleaningLevelCodes.sql new file mode 100644 index 0000000..28720ab --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CleaningLevelCodes.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[CleaningLevelCodes] ( + [Code] INT NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_CleaningLevelCodes] PRIMARY KEY CLUSTERED ([Code] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypes.sql new file mode 100644 index 0000000..8535922 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypes.sql @@ -0,0 +1,6 @@ +CREATE TABLE [dbo].[CollateralTypes] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + [LookupHandler] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_CollateralTypes] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypesToVehicleTypes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypesToVehicleTypes.sql new file mode 100644 index 0000000..59630fe --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralTypesToVehicleTypes.sql @@ -0,0 +1,6 @@ +CREATE TABLE [dbo].[CollateralTypesToVehicleTypes] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [CollateralTypeId] INT NOT NULL, + [VehicleTypeId] INT NOT NULL, + CONSTRAINT [PK_CollateralTypesToVehicleTypes] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralValuations.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralValuations.sql new file mode 100644 index 0000000..49d21ab --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/CollateralValuations.sql @@ -0,0 +1,47 @@ +CREATE TABLE [dbo].[CollateralValuations] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Year] INT NOT NULL, + [Make] NVARCHAR (100) NOT NULL, + [Model] NVARCHAR (100) NOT NULL, + [Uid] NVARCHAR (50) NULL, + [UcgVehicleId] NVARCHAR (50) NULL, + [LookupDate] DATETIME NULL, + [LowAdjustedAuctionValue] INT NULL, + [AverageAdjustedAuctionValue] INT NULL, + [HighAdjustedAuctionValue] INT NULL, + [AvgTradeIn] DECIMAL (18, 2) NULL, + [AvgTradeInPlusVinAcc] DECIMAL (18, 2) NULL, + [AvgTradeInPlusVinAccMileage] DECIMAL (18, 2) NULL, + [PercentOf] DECIMAL (5, 2) NULL, + [Condition] DECIMAL (5, 2) NULL, + [CalculatedFloorPrice] DECIMAL (18, 2) NULL, + [Mileage_Hours] NVARCHAR (100) NULL, + [CalculatedMMRValue] DECIMAL (18, 2) NULL, + [MakeCode] INT NULL, + [MakeDescription] VARCHAR (100) NULL, + [SeriesCode] INT NULL, + [SeriesDescription] VARCHAR (50) NULL, + [BodyCode] INT NULL, + [BodyDescription] VARCHAR (50) NULL, + [Weight] INT NULL, + [Msrp] DECIMAL (18, 2) NULL, + [AveMileage] INT NULL, + [MileageAdjustment] DECIMAL (18, 2) NULL, + [TradeIn] DECIMAL (18, 2) NULL, + [TradeInPlusVinAcc] DECIMAL (18, 2) NULL, + [TradeInPlusVinAccMileage] DECIMAL (18, 2) NULL, + [Loan] DECIMAL (18, 2) NULL, + [LoanPlusVinAcc] DECIMAL (18, 2) NULL, + [LoanPlusVinAccMileage] DECIMAL (18, 2) NULL, + [Retail] DECIMAL (18, 2) NULL, + [RetailPlusVinAcc] DECIMAL (18, 2) NULL, + [RetailPlusVinAccMileage] DECIMAL (18, 2) NULL, + [RoughTradeIn] DECIMAL (18, 2) NULL, + [RoughTradeInPlusVinAcc] DECIMAL (18, 2) NULL, + [RoughTradeInPlusVinAccMileage] DECIMAL (18, 2) NULL, + [LookupErrors] NVARCHAR (1024) NULL, + [ServiceTypeName] VARCHAR (100) NULL, + [AuctionInfoUnavailable] BIT NULL, + [DateRange] NCHAR(50) NULL, + CONSTRAINT [PK_CollateralValuations] PRIMARY KEY ([ID]) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Collaterals.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Collaterals.sql new file mode 100644 index 0000000..9b46c5b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Collaterals.sql @@ -0,0 +1,17 @@ +CREATE TABLE [dbo].[Collaterals] ( + [CollateralId] INT IDENTITY (1, 1) NOT NULL, + [RepoId] INT NOT NULL, + [VIN] NVARCHAR (100) NULL, + [Make] NVARCHAR (100) NULL, + [Model] NVARCHAR (100) NULL, + [Year] INT NULL, + [Description] NVARCHAR (255) NULL, + [VehicleType] INT NULL, + [BodyStyle] NVARCHAR (50) NULL, + [Inoperable] BIT NULL, + [RetitledInLandmarkName] DATETIME NULL, + [RefCollateralId] NVARCHAR (25) NULL, + [CollateralTypeId] INT NULL, + CONSTRAINT [PK_dbo.Collaterals] PRIMARY KEY CLUSTERED ([CollateralId] ASC), + CONSTRAINT [FK_dbo.Collaterals_dbo.Repoes_RepoId] FOREIGN KEY ([RepoId]) REFERENCES [dbo].[Repos] ([RepoId]) ON DELETE CASCADE +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/DefinedServiceFloorPricingParams.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/DefinedServiceFloorPricingParams.sql new file mode 100644 index 0000000..557e583 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/DefinedServiceFloorPricingParams.sql @@ -0,0 +1,10 @@ +CREATE TABLE [dbo].[DefinedServiceFloorPricingParams] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [LowCondition] DECIMAL (18, 2) NOT NULL, + [HighCondition] DECIMAL (18, 2) NOT NULL, + [FirstValueRange] VARCHAR (50) NOT NULL, + [SecondValueRange] VARCHAR (50) NOT NULL, + [PercentageOf] DECIMAL (18, 2) NOT NULL, + [ServiceType] INT NOT NULL, + CONSTRAINT [PK_DefinedServiceFloorPricingParams] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/EventAlert.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/EventAlert.sql new file mode 100644 index 0000000..23a4e46 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/EventAlert.sql @@ -0,0 +1,14 @@ +CREATE TABLE [dbo].[EventAlert] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [RepoId] INT NULL, + [AccountNo] NVARCHAR (50) NULL, + [DateAdded] DATETIME NOT NULL, + [DateMarkedRead] DATETIME NULL, + [CreatedBy] NVARCHAR (25) NOT NULL, + [MarkedReadBy] NVARCHAR (25) NULL, + [DayCount] INT NULL, + [Message] NVARCHAR (150) NULL, + [SubMessage] NVARCHAR (500) NULL, + [EmailRecipient] NVARCHAR(150) NULL, + [EmailBody] NVARCHAR(500) NULL +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/FloorPriceValuesUsed.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/FloorPriceValuesUsed.sql new file mode 100644 index 0000000..0581006 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/FloorPriceValuesUsed.sql @@ -0,0 +1,23 @@ +CREATE TABLE [dbo].[FloorPriceValuesUsed] ( + [AuctionAssignmentId] INT NOT NULL, + [CollateralValuationId] INT NOT NULL, + [RepoId] INT NULL, + [Condition] DECIMAL NULL, + [LowParam] DECIMAL(18, 1) NOT NULL, + [HighParam] DECIMAL(18, 1) NOT NULL, + [PercentageOf] DECIMAL(18, 2) NOT NULL, + [AuctionGuideBookPrice] DECIMAL(18, 2) NULL, + [WasAuctionGuideBookPriceUsed] BIT NULL, + [MMRNationalAdjustedPrice] DECIMAL(18, 2) NULL, + [MMRRegionalAdjustedPrice] DECIMAL(18, 2) NULL, + [WasMMRNationalAdjustedPriceUsed] BIT NULL, + [WasMMRRegionalAdjustedPriceUsed] BIT NULL, + [NADALowAdjustedAuctionValue] INT NOT NULL, + [NADAAverageAdjustedAuctionValue] INT NOT NULL, + [NADAHighAdjustedAuctionValue] INT NOT NULL, + [WasNADALowAdjustedAuctionValueUsed] BIT NOT NULL, + [WasNADAAverageAdjustedAuctionValueUsed] BIT NOT NULL, + [WasNADAHighAdjustedAuctionValueUsed] BIT NOT NULL, + [NADAMMRFloorPrice] DECIMAL(18, 2) NULL, + CONSTRAINT [PK_FloorPriceValuesUsed] PRIMARY KEY CLUSTERED ([AuctionAssignmentId], [CollateralValuationId]) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/HoldStatusCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/HoldStatusCodes.sql new file mode 100644 index 0000000..4864ee0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/HoldStatusCodes.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[HoldStatusCodes] ( + [Code] INT NOT NULL, + [Description] NVARCHAR (50) NOT NULL, + CONSTRAINT [PK_HoldStatusCodes] PRIMARY KEY CLUSTERED ([Code] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/IMSAutoFileData.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/IMSAutoFileData.sql new file mode 100644 index 0000000..3567117 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/IMSAutoFileData.sql @@ -0,0 +1,66 @@ +CREATE TABLE [dbo].[IMSAutoFileData] ( + [RepoId] INT NOT NULL, + [VIN] NVARCHAR (50) NOT NULL, + [AuctionAssignmentId] INT CONSTRAINT [DF_IMSAutoFileData_AuctionAssignmentID] DEFAULT ((0)) NULL, + [AnnouncedConditions] VARCHAR (1024) NULL, + [ArbitrationReason] VARCHAR (255) NULL, + [AsIs100KMiles] BIT NULL, + [AssignmentAccepted] DATETIME NULL, + [AuctionAge] INT NULL, + [AuctionGuideBookPrice] DECIMAL (18, 2) NULL, + [BuyerId] VARCHAR (100) NULL, + [BuyerName] VARCHAR (255) NULL, + [ChargeAmount] DECIMAL (18, 2) NULL, + [CheckAmount] DECIMAL (18, 2) NULL, + [ClientAge] INT NULL, + [ClientCancelled] DATETIME NULL, + [CollisionDamage] VARCHAR (255) NULL, + [AuctionCustomerNo] NVARCHAR (100) NOT NULL, + [InspectedDate] DATETIME NULL, + [DateofLoss] DATETIME NULL, + [DatePickedUp] DATETIME NULL, + [DateEnteredAutoIMS] DATETIME NULL, + [DaysRun] INT NULL, + [DaysRunHighestBidRecordedbyAuction] VARCHAR (255) NULL, + [DaystoDelivery] INT NULL, + [Description] VARCHAR (1024) NULL, + [IsDrivable] BIT NULL, + [FloorPriceAccepted] DECIMAL (18, 2) NULL, + [FundsSent] DATETIME NULL, + [GrossPercentFloor] DECIMAL (18, 2) NULL, + [HighestBid] DECIMAL (18, 2) NULL, + [StatedMileage] INT NULL, + [NetPercentFloor] DECIMAL (18, 2) NULL, + [OtherAdvanceFees] DECIMAL (18, 2) NULL, + [OTHERTotal] NVARCHAR (255) NULL, + [OTHERAdvancedOther] NVARCHAR (255) NULL, + [OTHERAdvancedStorage] NVARCHAR (255) NULL, + [OTHERAdvancedTow] NVARCHAR (255) NULL, + [OTHERMiscellaneous] NVARCHAR (255) NULL, + [SalePrice] DECIMAL (18, 2) NULL, + [ScheduledSaleDate] DATETIME NULL, + [SoldDate] DATETIME NULL, + [TitleNumber] VARCHAR (100) NULL, + [TitleReceived] DATETIME NULL, + [TitleReturned] DATETIME NULL, + [TitleState] NVARCHAR (50) NULL, + [TitleStatus] NVARCHAR (100) NULL, + [TitleStatusfromAuction] NVARCHAR (100) NULL, + [TotalCharges] DECIMAL (18, 2) NULL, + [ApprovedVehicleGrade] DECIMAL (5, 2) NULL, + [IsOperable] BIT NULL, + [ShipmentDate] DATETIME NULL, + [Status] NVARCHAR (100) NULL, + [StorageCost] DECIMAL (18, 2) NULL, + [StorageRatePerDay] DECIMAL (18, 2) NULL, + [TowCost] DECIMAL (18, 2) NULL, + [DateReceivedFromAutoIMS] DATETIME NULL, + [VehicleDecimalGradeCode] DECIMAL (18, 2) NULL, + [VehicleGrade] DECIMAL (18, 2) NULL, + [MMR] NVARCHAR (100) NULL, + [ConditionReportLink] NVARCHAR (255) NULL, + [CollateralImagePath] NVARCHAR (255) NULL, + [MMRNationalAdjustedPrice] DECIMAL (18, 2) NULL, + [MMRRegionalAdjustedPrice] DECIMAL (18, 2) NULL, + CONSTRAINT [PK_IMSAutoFileData_1] PRIMARY KEY CLUSTERED ([VIN] ASC, [RepoId] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Letters.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Letters.sql new file mode 100644 index 0000000..e7fc497 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Letters.sql @@ -0,0 +1,9 @@ +CREATE TABLE [dbo].[Letters] ( + [ActionCode] NVARCHAR (50) NOT NULL, + [LetterCode] NVARCHAR (10) NULL, + [Description] NVARCHAR (100) NOT NULL, + [DaysToWait] INT NULL, + [WhenLetterIsSent] NVARCHAR (50) NULL, + [LetterDescription] NVARCHAR (50) NULL, + CONSTRAINT [PK_Letters] PRIMARY KEY CLUSTERED ([ActionCode] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LoanCategories.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LoanCategories.sql new file mode 100644 index 0000000..fdb7125 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LoanCategories.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[LoanCategories] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [CategoryName] NVARCHAR (50) NOT NULL, + CONSTRAINT [PK_LoanCategories] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LocationTypeCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LocationTypeCodes.sql new file mode 100644 index 0000000..620ed0c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/LocationTypeCodes.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[LocationTypeCodes] ( + [Code] INT NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_LocationTypeCodes] PRIMARY KEY CLUSTERED ([Code] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/ProcessingReceipt.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/ProcessingReceipt.sql new file mode 100644 index 0000000..4f62cf1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/ProcessingReceipt.sql @@ -0,0 +1,11 @@ +/* columns are wide to accomodate bad data such as a CC number as a accountNo */ +CREATE TABLE [dbo].[ProcessingReceipt] ( + [AccountNo] NVARCHAR (20) NOT NULL, + [AccountId] NVARCHAR (20) NOT NULL DEFAULT '', + [CaseId] NVARCHAR (20) NOT NULL DEFAULT '', + [QueuedBy] NVARCHAR (40) NOT NULL, + [Outcome] NVARCHAR (25) NOT NULL, + [Message] NVARCHAR (125) NOT NULL, + [Processed] DATETIME NOT NULL, + CONSTRAINT [PK_PROCESSINGRECEIPT] PRIMARY KEY CLUSTERED ([AccountNo],[AccountId],[CaseId] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RefundStatuses.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RefundStatuses.sql new file mode 100644 index 0000000..9e0dc58 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RefundStatuses.sql @@ -0,0 +1,9 @@ +CREATE TABLE [dbo].[RefundStatuses] ( + [RepoId] INT NOT NULL, + [WarrantyRefundStatus] NVARCHAR (50) NULL, + [InsuranceRefundStatus] NVARCHAR (50) NULL, + [PremiumDefiRefundStatus] NVARCHAR (50) NULL, + [CPIRefundStatus] NVARCHAR (50) NULL, + [RefundsReceivedDate] DATETIME NULL, + CONSTRAINT [PK_RefundStatuses] PRIMARY KEY CLUSTERED ([RepoId] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompany.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompany.sql new file mode 100644 index 0000000..39b9d37 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompany.sql @@ -0,0 +1,17 @@ +CREATE TABLE [dbo].[RepoCompany] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Name] NVARCHAR (100) NOT NULL, + [Email] NVARCHAR (255) NULL, + [Address_Line_1] NVARCHAR (255) NULL, + [Address_Line_2] NVARCHAR (255) NULL, + [City] NVARCHAR (255) NULL, + [State] NVARCHAR (255) NULL, + [ZIP] NVARCHAR (10) NULL, + [Country] NVARCHAR (255) NULL, + [Phone] NVARCHAR (255) NULL, + [Fax] NVARCHAR (255) NULL, + [DisabledDate] DATETIME NULL, + [DisabledBy] NVARCHAR (255) NULL, + [ReferenceId] INT NULL, + CONSTRAINT [PK_RepoCompany] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompanySelections.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompanySelections.sql new file mode 100644 index 0000000..ef470b3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoCompanySelections.sql @@ -0,0 +1,10 @@ +CREATE TABLE [dbo].[RepoCompanySelections] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [CollateralId] INT NULL, + [RepoType] NVARCHAR (25) NULL, + [RepoCompanyId] INT NULL, + [RepoCompanyAssignmentDate] DATETIME NULL, + [DateRepoed] DATETIME NULL, + [DateTimeCreated] DATETIME DEFAULT (getdate()) NULL, + CONSTRAINT [PK_RepoCompanySelections] PRIMARY KEY CLUSTERED ([Id] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoManualStatus.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoManualStatus.sql new file mode 100644 index 0000000..bfd4285 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoManualStatus.sql @@ -0,0 +1,7 @@ +CREATE TABLE [dbo].[RepoManualStatus] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Description] NVARCHAR (50) NOT NULL, + [IsActiveStatus] BIT NULL, + [WatchForStatusChanges] BIT NULL, + CONSTRAINT [PK_RepoManualStatus] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoStatusChanges.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoStatusChanges.sql new file mode 100644 index 0000000..86425b2 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoStatusChanges.sql @@ -0,0 +1,9 @@ +CREATE TABLE [dbo].[RepoStatusChanges] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [RepoId] INT NOT NULL, + [StatusFrom] VARCHAR (20) NOT NULL, + [StatusTo] VARCHAR (20) NOT NULL, + [ChangedBy] VARCHAR (50) NOT NULL, + [ChangedDate] DATETIME NOT NULL, + CONSTRAINT [PK_RepoStatusChanges] PRIMARY KEY CLUSTERED ([Id] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncBatch.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncBatch.sql new file mode 100644 index 0000000..c855a62 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncBatch.sql @@ -0,0 +1,10 @@ +CREATE TABLE [dbo].[RepoSyncBatch]( + [RepoSyncBatchId] [int] NOT NULL PRIMARY KEY IDENTITY, + [RepoSyncRunInfoId] [int] NOT NULL, + [AuditItemKey] VARCHAR(50) NOT NULL, + [RepoSyncCompletionDateTime] [datetime] NULL, + [Outcome] [nvarchar](max) NULL, + [Message] [nvarchar](max) NULL, + [QueuedBy] [nvarchar](100) NOT NULL, + [AccountId] [nvarchar](100) NOT NULL +) diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncRunInfo.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncRunInfo.sql new file mode 100644 index 0000000..291013c --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/RepoSyncRunInfo.sql @@ -0,0 +1,8 @@ +CREATE TABLE [dbo].[RepoSyncRunInfo]( + [RepoSyncRunInfoId] [int] NOT NULL PRIMARY KEY IDENTITY, + [RepoStartDateTime] [datetime] NOT NULL, + [RepoEndDateTime] [datetime] NULL, + [RepoSyncCompletedCount] [int] NOT NULL DEFAULT 0, + [RepoSyncTotalCount] [int] NOT NULL, + [InitialRequest] varchar(max) NOT NULL +) diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Repos.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Repos.sql new file mode 100644 index 0000000..6a4d8fa --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/Repos.sql @@ -0,0 +1,41 @@ +CREATE TABLE [dbo].[Repos] ( + [RepoId] INT IDENTITY (1, 1) NOT NULL, + [AccountNo] NVARCHAR (100) NOT NULL, + [MemberNo] BIGINT NOT NULL, + [LoanNo] BIGINT NOT NULL, + [AccountTypeCode] NVARCHAR (50) NULL, + [Branch] INT NULL, + [Collector] NVARCHAR (50) NULL, + [Status] NVARCHAR (50) NULL, + [Balance] DECIMAL (18, 2) NULL, + [DueToPayDate] DATETIME NULL, + [O4RDate] DATETIME NULL, + [DateFirstDelinquent] DATETIME NULL, + [DelinquentAmount] DECIMAL (18, 2) NULL, + [DaysDelinquent] INT NULL, + [PaidSkipAmount] DECIMAL (18, 2) NULL, + [SubStatus] NVARCHAR (50) NULL, + [RequiredPaymentAmount] DECIMAL (18, 2) NULL, + [CurrentDelinquentAmount] DECIMAL (18, 2) NULL, + [LstPmtAmt] DECIMAL (18, 2) NULL, + [OrigAmount] DECIMAL (18, 2) NULL, + [LastPaymentDate] DATETIME NULL, + [DateFirstIntellect] DATETIME NULL, + [PrimaryIndividualId] NVARCHAR (100) NULL, + [Bankruptcy] NVARCHAR (50) NULL, + [ManualRedemptionDate] DATETIME NULL, + [RedemptionExpiredDate] DATETIME NULL, + [DateClosed] DATETIME NULL, + [WrittenOffDate] DATETIME NULL, + [ReferenceStatus] NVARCHAR (50) NULL, + [ReferenceId] INT NULL, + [ReferenceCaseId] INT NULL, + [AccountLastProcessed] DATETIME NULL, + [OrigAccountTypeCode] NVARCHAR(50) NULL, + [SixtyPlusCollectorId] INT NULL, + CONSTRAINT [PK_dbo.Repoes] PRIMARY KEY CLUSTERED ([RepoId] ASC), + CONSTRAINT [FK_dbo.Repos_dbo.SixtyPlusCollector_SixtyPlusCollectorId] FOREIGN KEY ([SixtyPLusCollectorId]) REFERENCES [SixtyPlusCollector]([SixtyPLusCollectorId]) +); +GO +CREATE NONCLUSTERED INDEX [NonClusteredIndex-20170405-122422] + ON [dbo].[Repos]([AccountNo] ASC); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SavedLocations.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SavedLocations.sql new file mode 100644 index 0000000..33f5948 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SavedLocations.sql @@ -0,0 +1,14 @@ +CREATE TABLE [dbo].[SavedLocations] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Name] NVARCHAR (255) NULL, + [LocationType] INT NULL, + [Address_Line_1] NVARCHAR (255) NULL, + [Address_Line_2] NVARCHAR (255) NULL, + [City] NVARCHAR (255) NULL, + [State] NVARCHAR (255) NULL, + [ZIP] NVARCHAR (10) NULL, + [Country] NVARCHAR (255) NULL, + [Phone] NVARCHAR (255) NULL, + [Fax] NVARCHAR (255) NULL, + CONSTRAINT [PK_SavedLocations] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SixtyPlusCollector.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SixtyPlusCollector.sql new file mode 100644 index 0000000..d207278 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/SixtyPlusCollector.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[SixtyPlusCollector] ( + [SixtyPlusCollectorId] INT IDENTITY NOT NULL, + [SixtyPlusCollectorName] NVARCHAR(50) NOT NULL + PRIMARY KEY (SixtyPlusCollectorId) + ); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/UserCache.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/UserCache.sql new file mode 100644 index 0000000..604ccd4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/UserCache.sql @@ -0,0 +1,7 @@ +CREATE TABLE [dbo].[UserCache] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [ReferenceId] INT NOT NULL UNIQUE, + [Name] NVARCHAR (100) NOT NULL, + [Code] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_UserCache] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleFloorPricingParameters.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleFloorPricingParameters.sql new file mode 100644 index 0000000..8392bf1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleFloorPricingParameters.sql @@ -0,0 +1,11 @@ +CREATE TABLE [dbo].[VehicleFloorPricingParameters] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [LowCondition] DECIMAL (18, 1) NOT NULL, + [HighCondition] DECIMAL (18, 1) NOT NULL, + [NADAFirstValue] NVARCHAR (50) NOT NULL, + [NADASecondValue] NVARCHAR (50) NOT NULL, + [PercentageOf] DECIMAL (18, 2) NOT NULL, + [ValuationServiceType] INT CONSTRAINT [DF_VehicleFloorPricingParameters_ValueationServiceType] DEFAULT ((0)) NOT NULL, + [MMRAdjustedValue] NVARCHAR (50) NULL, + CONSTRAINT [PK_VehiclePricingParameters] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleTypeCodes.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleTypeCodes.sql new file mode 100644 index 0000000..3679b80 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehicleTypeCodes.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[VehicleTypeCodes] ( + [Code] NVARCHAR (100) NOT NULL, + [Description] NVARCHAR (100) NOT NULL, + CONSTRAINT [PK_VehicleTypeCodes] PRIMARY KEY CLUSTERED ([Code] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehiclesReturnedFromLookup.sql b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehiclesReturnedFromLookup.sql new file mode 100644 index 0000000..9ccd3c7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Api.Internal.RepoApiSql/dbo/Tables/VehiclesReturnedFromLookup.sql @@ -0,0 +1,52 @@ +CREATE TABLE [dbo].[VehiclesReturnedFromLookup] ( + [ID] INT IDENTITY (1, 1) NOT NULL, + [Year] INT NOT NULL, + [Make] NVARCHAR (100) NOT NULL, + [Model] NVARCHAR (100) NOT NULL, + [Options] NVARCHAR (100) NOT NULL, + [Uid] NVARCHAR (50) NULL, + [UcgVehicleId] NVARCHAR (50) NULL, + [CollateralId] INT NOT NULL, + [LookupDate] DATETIME NULL, + [NADA_Auction_Value_One] DECIMAL (18, 2) NULL, + [NADA_Auction_Value_Two] DECIMAL (18, 2) NULL, + [NADA_Auction_Value_Three] DECIMAL (18, 2) NULL, + [NADA_Trade_Prices_One] DECIMAL (18, 2) NULL, + [NADA_Trade_Prices_Two] DECIMAL (18, 2) NULL, + [NADA_Trade_Prices_three] DECIMAL (18, 2) NULL, + [MMR_First_Value_Type] NVARCHAR (50) NULL, + [Price_Type_Used] NVARCHAR (50) NULL, + [NADA_First_Value_Type] NVARCHAR (50) NULL, + [NADA_Second_Value_Type] NVARCHAR (50) NULL, + [PercentOf] DECIMAL (5, 2) NULL, + [Condition] DECIMAL (5, 2) NULL, + [CalculatedFloorPrice] DECIMAL (18, 2) NULL, + [Mileage_Hours] NVARCHAR (100) NULL, + [MMR_Second_Value_Type] NVARCHAR (50) NULL, + [CalculatedMMRValue] DECIMAL (18, 2) NULL, + [MakeCode] INT NULL, + [MakeDescription] VARCHAR (100) NULL, + [SeriesCode] INT NULL, + [SeriesDescription] VARCHAR (50) NULL, + [BodyCode] INT NULL, + [BodyDescription] VARCHAR (50) NULL, + [Weight] INT NULL, + [Msrp] DECIMAL (18, 2) NULL, + [AveMileage] INT NULL, + [MileageAdjustment] DECIMAL (18, 2) NULL, + [TradeIn] DECIMAL (18, 2) NULL, + [TradeInPlusVinAcc] DECIMAL (18, 2) NULL, + [TradeInPlusVinAccMileage] DECIMAL (18, 2) NULL, + [Loan] DECIMAL (18, 2) NULL, + [LoanPlusVinAcc] DECIMAL (18, 2) NULL, + [LoanPlusVinAccMileage] DECIMAL (18, 2) NULL, + [Retail] DECIMAL (18, 2) NULL, + [RetailPlusVinAcc] DECIMAL (18, 2) NULL, + [ReteailPlusVinAccMileage] DECIMAL (18, 2) NULL, + [RoughTradeIn] DECIMAL (18, 2) NULL, + [RoughTradeInVinAcc] DECIMAL (18, 2) NULL, + [RoughTradeInAccMileage] DECIMAL (18, 2) NULL, + [LookupErrors] NVARCHAR (1024) NULL, + [ServiceTypeName] VARCHAR (100) NULL, + CONSTRAINT [PK_VehiclesReturnedFromLookup] PRIMARY KEY CLUSTERED ([ID] ASC) +); \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/LCU.Library.Internal.RepoApiModels.csproj b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/LCU.Library.Internal.RepoApiModels.csproj new file mode 100644 index 0000000..38468bf --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/LCU.Library.Internal.RepoApiModels.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + latest + True + 1.0.0 + + 1.0.0 - Initial Creation of Models Library. + + + 1.0.0 - Initial Creation of Models Library. + + + + + + + + + diff --git a/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncBatch.cs b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncBatch.cs new file mode 100644 index 0000000..45d130a --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncBatch.cs @@ -0,0 +1,22 @@ +using System; + +namespace LCU.Library.Internal.RepoApiModels.Models +{ + public class RepoSyncBatch + { + public int RepoSyncBatchId { get; set; } + public int RepoSyncRunInfoId { get; set; } + public string AuditItemKey { get; set; } + public DateTime? RepoSyncCompletionDateTime { get; set; } + public string Outcome { get; set; } + public string Message { get; set; } + public string QueuedBy { get; set; } + public string AccountId { get; set; } + + public string Pretty() + { + var parts = AuditItemKey.Split(':'); + return $"Key: {parts[0]}|{AccountId}|{parts[1]}|{QueuedBy} -> {Outcome} : {Message}"; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncRunInfo.cs b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncRunInfo.cs new file mode 100644 index 0000000..c3c8401 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/RepoSyncRunInfo.cs @@ -0,0 +1,20 @@ +using LCU.Library.Internal.ApiResponse.Models; +using System; +using System.ComponentModel.DataAnnotations; +using System.Reflection; + + + +namespace LCU.Library.Internal.RepoApiModels.Models +{ + public class RepoSyncRunInfo + { + [Key] + public int RepoSyncRunInfoId { get; set; } + public DateTime RepoStartDateTime { get; set; } + public DateTime? RepoEndDateTime { get; set; } + public int RepoSyncCompletedCount { get; set; } + public int RepoSyncTotalCount { get; set; } + public string InitialRequest { get; set; } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoAccountProcessingRequest.cs b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoAccountProcessingRequest.cs new file mode 100644 index 0000000..b115fa3 --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoAccountProcessingRequest.cs @@ -0,0 +1,16 @@ +using System; + +namespace LCU.Library.Internal.RepoApiModels.Models.Requests +{ + public class RepoAccountProcessingRequest + { + public string AccountNo { get; set; } + public string AccountId { get; set; } + public string CaseId { get; set; } + public string QueuedBy { get; set; } + public string DetermineAuditItemKey() + { + return AccountNo + ":" + CaseId; + } + } +} diff --git a/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoBatchRequest.cs b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoBatchRequest.cs new file mode 100644 index 0000000..f2f495b --- /dev/null +++ b/LCU.API.Internal.RepoApi/LCU.Library.Internal.RepoApiModels/Models/Requests/RepoBatchRequest.cs @@ -0,0 +1,8 @@ +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.RepoApiModels.Models.Requests +{ + public class RepoBatchRequest : ReturnResponse + { + } +} diff --git a/LCU.API.Internal.RepoApi/README.md b/LCU.API.Internal.RepoApi/README.md new file mode 100644 index 0000000..e37e4b1 --- /dev/null +++ b/LCU.API.Internal.RepoApi/README.md @@ -0,0 +1,20 @@ +# Introduction +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +# Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +1. Installation process +2. Software dependencies +3. Latest releases +4. API references + +# Build and Test +TODO: Describe and show how to build your code and run the tests. + +# Contribute +TODO: Explain how other users and developers can contribute to make your code better. + +If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: +- [ASP.NET Core](https://github.com/aspnet/Home) +- [Visual Studio Code](https://github.com/Microsoft/vscode) +- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/azure-k8s-pipeline.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-k8s-pipeline.yaml new file mode 100644 index 0000000..cd888e7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-k8s-pipeline.yaml @@ -0,0 +1,65 @@ +parameters: +- name: target_environment + displayName: Target Environment + type: string + default: qa + values: + - qa + - uat + - prd +- name: k8s_namespace + displayName: K8s Namespace to deploy to + type: string + default: landmarkcu + values: + - landmarkcu +variables: +- template: ./config-files/${{ parameters.target_environment }}-pvars.yaml +- name: source_branch_name + value: $[replace(replace(variables['Build.SourceBranch'], 'refs/heads/', ''), '/', '-')] +- name: docker_container_repository_name + value: landmarkcu/lcu.api.internal.repoapi-repoapi +resources: + repositories: + - repository: azdo_pipeline_templates + type: git + name: Infrastructure Platform/azdo-pipeline-templates + ref: main +stages: +# DOTNET BUILD AND NUGET PUSH +- template: dotnet-nuget-build-push.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool_nuget) + azure_artifacts_feed: 'LCU-Internal' +# DACPAC BUILD AND PUSH +- template: db-build-deploy.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool_db) + azdo_deployment_environment: $(pvar_azdo_deployment_environment_db) + sql_project_path: './LCU.Api.Internal.RepoApiSql/LCU.Api.Internal.RepoApiSql.sqlproj' + built_sql_dacpac_file_path: './LCU.Api.Internal.RepoApiSql/bin/Release/LCU.Api.Internal.RepoApiSql.dacpac' + name_of_artifact: evaluation_dacpac + db_name: $(pvar_RepoApi_db_name) + sql_server_name: $(pvar_RepoApi_sql_server_name) +# DOCKER BUILD AND PUSH +- template: docker-build-push.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool) + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_container_repository_name: $(docker_container_repository_name) + dockerfile_path: ./Dockerfile + docker_image_tags: | + build-$(Build.BuildId) + $(source_branch_name) +# K8S DEPLOY +- template: k8s-deploy.yaml@azdo_pipeline_templates + parameters: + dependsOn: CI + condition: or(eq(variables['Build.SourceBranchName'], 'develop'), eq(variables['Build.SourceBranchName'], 'main')) + azdo_agent_pool: $(pvar_azdo_agent_pool) + kubectl_version: $(pvar_kubectl_version) + k8s_service_connection: $(pvar_k8s_service_connection) + k8s_namespace: ${{ parameters.k8s_namespace }} + k8s_kustomization_path: ./kustomize/overlays/${{ parameters.target_environment }} + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_image_to_deploy: $(docker_container_repository_name):build-$(Build.BuildId) diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-feature-branches.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-feature-branches.yaml new file mode 100644 index 0000000..ee44aef --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-feature-branches.yaml @@ -0,0 +1,10 @@ +trigger: + branches: + include: + - '*' + exclude: + - 'main' + - 'develop' +extends: + template: azure-k8s-pipeline.yaml + diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-prd.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-prd.yaml new file mode 100644 index 0000000..7d13684 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-prd.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'main' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: prd + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-qa.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-qa.yaml new file mode 100644 index 0000000..f581dd4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-qa.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'develop' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: qa + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-uat.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-uat.yaml new file mode 100644 index 0000000..4747234 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/azure-pipelines-uat.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'main' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: uat + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/prd-pvars.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/prd-pvars.yaml new file mode 100644 index 0000000..4371066 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/prd-pvars.yaml @@ -0,0 +1,11 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-prd-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-prd-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-PRD + pvar_RepoApi_db_name: REPODB + pvar_RepoApi_sql_server_name: D1CLS01P diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/qa-pvars.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/qa-pvars.yaml new file mode 100644 index 0000000..707f285 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/qa-pvars.yaml @@ -0,0 +1,11 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-qa-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-qa-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-QA + pvar_RepoApi_db_name: REPODB + pvar_RepoApi_sql_server_name: D1DTB01Q diff --git a/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/uat-pvars.yaml b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/uat-pvars.yaml new file mode 100644 index 0000000..aa04977 --- /dev/null +++ b/LCU.API.Internal.RepoApi/azdo_pipelines/config-files/uat-pvars.yaml @@ -0,0 +1,11 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-uat-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-uat-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-UAT + pvar_RepoApi_db_name: REPODB + pvar_RepoApi_sql_server_name: D1CLS01U \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/cleanbuildrun.sh b/LCU.API.Internal.RepoApi/cleanbuildrun.sh new file mode 100644 index 0000000..b26321d --- /dev/null +++ b/LCU.API.Internal.RepoApi/cleanbuildrun.sh @@ -0,0 +1,10 @@ +#!/bin/bash +echo "Building Repo Api Docker Image"; +dotnet clean +rm -rf LCU.API.Internal.RepoApi/bin +rm -rf LCU.API.Internal.RepoApi/obj +docker build -t landmarkcu/repo-api . +echo "Running Repo Api Docker Container"; +docker run -p 8080:80 landmarkcu/repo-api + + diff --git a/LCU.API.Internal.RepoApi/kustomize/base/Deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/base/Deployment.yaml new file mode 100644 index 0000000..2e78ce8 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/base/Deployment.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: repo-api +spec: + replicas: 1 + selector: + matchLabels: + app: repo-api + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: repo-api + spec: + volumes: [] + containers: + - name: repo-api + env: [] + volumeMounts: [] + image: repo-api + resources: + limits: + memory: "1536Mi" + cpu: "500m" + ports: + - containerPort: 8080 + livenessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: /api/status + port: 8080 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: /api/status + port: 8080 \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/base/Service.yaml b/LCU.API.Internal.RepoApi/kustomize/base/Service.yaml new file mode 100644 index 0000000..8ebe97b --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/base/Service.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: repo-api +spec: + selector: + app: repo-api + ports: + - port: 8080 + targetPort: 8080 \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/base/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..46319de --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/base/kustomization.yaml @@ -0,0 +1,9 @@ +resources: + - Deployment.yaml + - Service.yaml + - secrets-provider-class.yaml + - virtual-service.yaml + +images: +- name: repo-api + newName: landmarkcu/lcu.api.internal.repoapi-repoapi \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/base/secrets-provider-class.yaml b/LCU.API.Internal.RepoApi/kustomize/base/secrets-provider-class.yaml new file mode 100644 index 0000000..96cacb7 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/base/secrets-provider-class.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: repo-api-secrets-provider +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "true" + tenantId: 63cd1554-2783-4a86-ae50-b9e6c21c9499 \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/base/virtual-service.yaml b/LCU.API.Internal.RepoApi/kustomize/base/virtual-service.yaml new file mode 100644 index 0000000..567cd7d --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/base/virtual-service.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: repo-api +spec: + hosts: [] + gateways: + - istio-system/ingress-gateway + http: + - name: repo-api + match: + - uri: + prefix: / + route: + - destination: + host: repo-api + port: + number: 8080 diff --git a/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/kustomization.yaml new file mode 100644 index 0000000..0ae1552 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_secrets_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_deployment.yaml new file mode 100644 index 0000000..449e561 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_deployment.yaml @@ -0,0 +1,41 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: repo-api-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: normal-repo-api-secrets-provider + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: repo-api-secrets + mountPath: /vault/secrets/repo-api-seq-api-key.json + readOnly: true + subPath: repo-api-seq-api-key + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: repo-api-secrets + mountPath: /vault/secrets/repo-api-database-connection-string.json + readOnly: true + subPath: repo-api-database-connection-string + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: repo-api-secrets + mountPath: /vault/secrets/repo-api-queue-user-name.json + readOnly: true + subPath: repo-api-queue-user-name + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: repo-api-secrets + mountPath: /vault/secrets/repo-api-queue-password.json + readOnly: true + subPath: repo-api-queue-password \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_provider.yaml b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_provider.yaml new file mode 100644 index 0000000..5dae167 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/keyvaults/normal/set_secrets_provider.yaml @@ -0,0 +1,21 @@ +- op: add + path: /spec/parameters/objects + value: | + array: + - | + objectName: repo-api-seq-api-key + objectType: secret + objectVersion: "" + - | + objectName: repo-api-database-connection-string + objectType: secret + objectVersion: "" + - | + objectName: repo-api-queue-user-name + objectType: secret + objectVersion: "" + - | + objectName: repo-api-queue-password + objectType: secret + objectVersion: "" + diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/appsettings/appsettings.json b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/appsettings/appsettings.json new file mode 100644 index 0000000..6465b50 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/appsettings/appsettings.json @@ -0,0 +1,9 @@ +{ + "Primary": { + "FeatureFlags": { + "RunRepoAccountProcessingConsumer": false, + "RunRepoBatchProcessingConsumer": false, + "RunRepoBatchCompletionProcessingConsumer": true + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/kustomization.yaml new file mode 100644 index 0000000..75405fc --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/kustomization.yaml @@ -0,0 +1,40 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: completion- + +configMapGenerator: + - name: repo-api-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name + diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/set_appsettings_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/set_appsettings_deployment.yaml new file mode 100644 index 0000000..bd4cf00 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/completion/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: repo-api-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/workload-appsettings diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/appsettings/appsettings.json b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/appsettings/appsettings.json new file mode 100644 index 0000000..dedb29f --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/appsettings/appsettings.json @@ -0,0 +1,9 @@ +{ + "Primary": { + "FeatureFlags": { + "RunRepoAccountProcessingConsumer": true, + "RunRepoBatchProcessingConsumer": true, + "RunRepoBatchCompletionProcessingConsumer": false + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/kustomization.yaml new file mode 100644 index 0000000..d92785b --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/kustomization.yaml @@ -0,0 +1,40 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: normal- + +configMapGenerator: + - name: repo-api-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name + diff --git a/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/set_appsettings_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/set_appsettings_deployment.yaml new file mode 100644 index 0000000..bd4cf00 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/components/workloads/normal/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: repo-api-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/workload-appsettings diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/appsettings/appsettings.json b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/appsettings/appsettings.json new file mode 100644 index 0000000..a9de534 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/appsettings/appsettings.json @@ -0,0 +1,23 @@ +{ + "Primary": { + "SeqUrl": "https://seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://api.lcu-internal.com" + ], + "RepoManagerAccountUrl": "https://repomanage.lcu-internal.com/#/Account/" + }, + "ConnectionConfiguration": { + "HostUrl": "rabbitmq.lcu-internal.com" + }, + "CollectionsApiConfiguration": { + "Url": "https://collectionsapi.lcu-internal.com" + }, + "CoreApiWrapperConfiguration": { + "Url": "https://prd-core-api-wrapper-api.apps.azure.lcu-internal.com" + }, + "NotificationApiConfiguration": { + "Url": "https://notificationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/kustomization.yaml new file mode 100644 index 0000000..60f07bb --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/set_virtual_service.yaml new file mode 100644 index 0000000..5cb7cb0 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: prd-repo-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_appsettings_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_appsettings_deployment.yaml new file mode 100644 index 0000000..335b8be --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: "Production" \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_secrets_provider_class.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_secrets_provider_class.yaml new file mode 100644 index 0000000..816f14c --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/component/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 9eaefc49-e5d3-48d4-a2b9-b42cf2d8ca03 + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-prod-uscz diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/kustomization.yaml new file mode 100644 index 0000000..b869c70 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/kustomization.yaml @@ -0,0 +1,15 @@ +resources: + - normal + - completion + +configMapGenerator: + - name: repo-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/kustomization.yaml new file mode 100644 index 0000000..a1e6b06 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/set_virtual_service.yaml new file mode 100644 index 0000000..853ae1e --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: prd-repo-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/prd/set_configmap_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/set_configmap_deployment.yaml new file mode 100644 index 0000000..8017378 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/prd/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: repo-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/appsettings/appsettings.json b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/appsettings/appsettings.json new file mode 100644 index 0000000..8e02496 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/appsettings/appsettings.json @@ -0,0 +1,23 @@ +{ + "Primary": { + "SeqUrl": "https://qa-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ], + "RepoManagerAccountUrl": "https://qa-repomanage.lcu-internal.com/#/Account/" + }, + "ConnectionConfiguration": { + "HostUrl": "qa-rabbitmq.lcu-internal.com" + }, + "CollectionsApiConfiguration": { + "Url": "https://qa-collectionsapi.lcu-internal.com" + }, + "CoreApiWrapperConfiguration": { + "Url": "https://qa-core-api-wrapper-api.apps.azure.lcu-internal.com" + }, + "NotificationApiConfiguration": { + "Url": "https://qa-notificationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/kustomization.yaml new file mode 100644 index 0000000..60f07bb --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/set_virtual_service.yaml new file mode 100644 index 0000000..e747230 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: qa-repo-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_appsettings_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_appsettings_deployment.yaml new file mode 100644 index 0000000..818dec4 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: Development \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_secrets_provider_class.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_secrets_provider_class.yaml new file mode 100644 index 0000000..8719a77 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/component/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 3286ed9a-f85c-4c50-b742-dbd3c1c3fe1f + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-qa-uscz diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/kustomization.yaml new file mode 100644 index 0000000..b869c70 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/kustomization.yaml @@ -0,0 +1,15 @@ +resources: + - normal + - completion + +configMapGenerator: + - name: repo-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/kustomization.yaml new file mode 100644 index 0000000..a1e6b06 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/set_virtual_service.yaml new file mode 100644 index 0000000..f0f6077 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: qa-repo-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/qa/set_configmap_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/set_configmap_deployment.yaml new file mode 100644 index 0000000..8017378 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/qa/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: repo-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/appsettings/appsettings.json b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/appsettings/appsettings.json new file mode 100644 index 0000000..4754b4c --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/appsettings/appsettings.json @@ -0,0 +1,23 @@ +{ + "Primary": { + "SeqUrl": "https://uat-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://uat-api.lcu-internal.com" + ], + "RepoManagerAccountUrl": "https://uat-repomanage.lcu-internal.com/#/Account/" + }, + "ConnectionConfiguration": { + "HostUrl": "uat-rabbitmq.lcu-internal.com" + }, + "CollectionsApiConfiguration": { + "Url": "https://uat-collectionsapi.lcu-internal.com" + }, + "CoreApiWrapperConfiguration": { + "Url": "https://uat-core-api-wrapper-api.apps.azure.lcu-internal.com" + }, + "NotificationApiConfiguration": { + "Url": "https://uat-notificationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/kustomization.yaml new file mode 100644 index 0000000..60f07bb --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/set_virtual_service.yaml new file mode 100644 index 0000000..be0ea1c --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: uat-repo-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_appsettings_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_appsettings_deployment.yaml new file mode 100644 index 0000000..4fd0533 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: "Staging" \ No newline at end of file diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_secrets_provider_class.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_secrets_provider_class.yaml new file mode 100644 index 0000000..efe4103 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/component/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 64ba01ed-1ecc-4c9b-b56e-8d618f2b81ce + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-uat-uscz diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/kustomization.yaml new file mode 100644 index 0000000..b869c70 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/kustomization.yaml @@ -0,0 +1,15 @@ +resources: + - normal + - completion + +configMapGenerator: + - name: repo-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/kustomization.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/kustomization.yaml new file mode 100644 index 0000000..a1e6b06 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../component + - ../../../components/keyvaults/normal + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/set_virtual_service.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/set_virtual_service.yaml new file mode 100644 index 0000000..15b6117 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: uat-repo-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.RepoApi/kustomize/overlays/uat/set_configmap_deployment.yaml b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/set_configmap_deployment.yaml new file mode 100644 index 0000000..8017378 --- /dev/null +++ b/LCU.API.Internal.RepoApi/kustomize/overlays/uat/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: repo-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings