Adding
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
[*.{cs,csx,vb,vbx,razor}]
|
||||||
|
indent_size = 4
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
|
#typescript Output
|
||||||
|
app.js
|
||||||
|
app.js.map
|
||||||
|
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
.idea/**
|
||||||
|
.vscode/**
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# .NET Core SDK
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS aspnetcore-build
|
||||||
|
|
||||||
|
# Install Node.js + npm (Node 18 LTS, required for TypeScript)
|
||||||
|
RUN apt-get update && apt-get install -y curl gnupg \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
|
||||||
|
&& apt-get install -y nodejs
|
||||||
|
|
||||||
|
# Copy Projects
|
||||||
|
COPY LCU.WebApps.Internal.DocumentManager/LCU.WebApps.Internal.DocumentManager.csproj ./source/LCU.WebApps.Internal.DocumentManager/
|
||||||
|
|
||||||
|
# Install Credential Provider and set env variables to enable Nuget restore with auth
|
||||||
|
ARG PAT=vvqu3q252m5kqwptu4tavha4ghk4svhmwsahb2kvlu22ogiegt6q
|
||||||
|
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.WebApps.Internal.DocumentManager/LCU.WebApps.Internal.DocumentManager.csproj
|
||||||
|
|
||||||
|
# Copy All Files
|
||||||
|
COPY . ./source/
|
||||||
|
|
||||||
|
# .NET Core Publish
|
||||||
|
FROM aspnetcore-build AS publish
|
||||||
|
RUN dotnet publish ./source/LCU.WebApps.Internal.DocumentManager/LCU.WebApps.Internal.DocumentManager.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;
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /dist .
|
||||||
|
|
||||||
|
# ENVIRONMENT VALUES
|
||||||
|
ENV TZ=America/Chicago
|
||||||
|
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
|
||||||
|
ENV ASPNETCORE_ENVIRONMENT=Development
|
||||||
|
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||||
|
|
||||||
|
ENTRYPOINT ["dotnet", "LCU.WebApps.Internal.DocumentManager.dll"]
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.32112.339
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.WebApps.Internal.DocumentManager", "LCU.WebApps.Internal.DocumentManager\LCU.WebApps.Internal.DocumentManager.csproj", "{4213EB34-DE40-4371-B8BB-CF5E0B0C6646}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F274295E-D19F-4FDF-8495-16F18AE2D8ED}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.dockerignore = .dockerignore
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
.gitignore = .gitignore
|
||||||
|
azure-pipelines-iis.yml = azure-pipelines-iis.yml
|
||||||
|
azure-pipelines.yml = azure-pipelines.yml
|
||||||
|
Dockerfile = Dockerfile
|
||||||
|
README.md = README.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4213EB34-DE40-4371-B8BB-CF5E0B0C6646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4213EB34-DE40-4371-B8BB-CF5E0B0C6646}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4213EB34-DE40-4371-B8BB-CF5E0B0C6646}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4213EB34-DE40-4371-B8BB-CF5E0B0C6646}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {03D039D2-36F6-4479-AA1F-96A8BFC61E70}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"microsoft.dotnet-msidentity": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-msidentity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"microsoft.dotnet-openapi": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-openapi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<CascadingAuthenticationState>
|
||||||
|
<CascadingBlazoredModal DisableBackgroundCancel="true">
|
||||||
|
<Router AppAssembly="@typeof(App).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
<NotFound>
|
||||||
|
<PageTitle>Not found</PageTitle>
|
||||||
|
<LayoutView Layout="@typeof(MainLayout)">
|
||||||
|
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</NotFound>
|
||||||
|
</Router>
|
||||||
|
</CascadingBlazoredModal>
|
||||||
|
</CascadingAuthenticationState>
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.Identity.Client;
|
||||||
|
using Microsoft.Identity.Web;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
|
||||||
|
public enum DownstreamWebService
|
||||||
|
{
|
||||||
|
DocumentTypeRetriever,
|
||||||
|
DocumentTypeUploader,
|
||||||
|
DocumentIngestor
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AuthenticationHelper
|
||||||
|
{
|
||||||
|
private readonly AuthenticationStateProvider _authenticationStateProvider;
|
||||||
|
private readonly AppConfiguration _appConfiguration;
|
||||||
|
private readonly ITokenAcquisition _tokenAcquisition;
|
||||||
|
private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;
|
||||||
|
private readonly IAuthorizationService _authorizationService;
|
||||||
|
|
||||||
|
private const string ClientKeyName = "Client";
|
||||||
|
private const string ClientKeyValue = "DocumentManager";
|
||||||
|
private const string ApiKeyName = "ApiKey";
|
||||||
|
|
||||||
|
private readonly IDictionary<DownstreamWebService, string> _serviceScope;
|
||||||
|
|
||||||
|
public AuthenticationHelper
|
||||||
|
(
|
||||||
|
AuthenticationStateProvider authenticationStateProvider,
|
||||||
|
AppConfiguration appConfiguration,
|
||||||
|
ITokenAcquisition tokenAcquisition,
|
||||||
|
DocumentTypeRetrieverApiConfiguration documentTypeRetrieverConfiguration,
|
||||||
|
MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler,
|
||||||
|
IAuthorizationService authorizationService,
|
||||||
|
DocumentTypeUploaderApiConfiguration documentTypeUploaderApiConfiguration,
|
||||||
|
DocumentIngestorApiConfiguration documentIngestorApiConfiguration
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_authenticationStateProvider = authenticationStateProvider;
|
||||||
|
_appConfiguration = appConfiguration;
|
||||||
|
_tokenAcquisition = tokenAcquisition;
|
||||||
|
_consentHandler = consentHandler;
|
||||||
|
_authorizationService = authorizationService;
|
||||||
|
|
||||||
|
_serviceScope = new Dictionary<DownstreamWebService, string>
|
||||||
|
{
|
||||||
|
[DownstreamWebService.DocumentTypeRetriever] = documentTypeRetrieverConfiguration.Scope,
|
||||||
|
[DownstreamWebService.DocumentTypeUploader] = documentTypeUploaderApiConfiguration.Scope,
|
||||||
|
[DownstreamWebService.DocumentIngestor] = documentIngestorApiConfiguration.Scope,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClient AddApiSecurityHeaders(HttpClient client)
|
||||||
|
{
|
||||||
|
if (!client.DefaultRequestHeaders.Contains(ClientKeyName))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(ClientKeyName, ClientKeyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.DefaultRequestHeaders.Contains(ApiKeyName))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(ApiKeyName, _appConfiguration.DocumentManagerApiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddApiSecurityUserClaims(HttpClient client, DownstreamWebService service)
|
||||||
|
{
|
||||||
|
var token = await GetServiceToken(service);
|
||||||
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> GetServiceToken(DownstreamWebService service)
|
||||||
|
{
|
||||||
|
async Task<string?> GetToken()
|
||||||
|
{
|
||||||
|
return await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { _serviceScope[service] });
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await GetToken();
|
||||||
|
}
|
||||||
|
catch (MsalUiRequiredException e)
|
||||||
|
{
|
||||||
|
_consentHandler.HandleException(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (MicrosoftIdentityWebChallengeUserException e)
|
||||||
|
{
|
||||||
|
_consentHandler.HandleException(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to authenticate downstream web service: {Enum.GetName(service)}."
|
||||||
|
+ $"Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpClient> AddApiSecurityHeadersWithUserClaims(HttpClient client, DownstreamWebService service)
|
||||||
|
{
|
||||||
|
await AddApiSecurityUserClaims(client, service);
|
||||||
|
|
||||||
|
return AddApiSecurityHeaders(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, string>> CreateApiSecurityHeadersWithAuth(DownstreamWebService service)
|
||||||
|
{
|
||||||
|
var headers = new Dictionary<string, string>();
|
||||||
|
var token = await GetServiceToken(service);
|
||||||
|
headers.Add("Authorization", "Bearer " + token);
|
||||||
|
headers = CreateApiSecurityHeaders(headers);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, string> CreateApiSecurityHeaders(Dictionary<string, string> headers)
|
||||||
|
{
|
||||||
|
headers.Add(ClientKeyName, ClientKeyValue);
|
||||||
|
headers.Add(ApiKeyName, _appConfiguration.DocumentManagerApiKey);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool PolicySatisfied(string policy)
|
||||||
|
=> _authorizationService
|
||||||
|
.AuthorizeAsync(_authenticationStateProvider.GetAuthenticationStateAsync().Result.User, policy)
|
||||||
|
.Result.Succeeded;
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class AppConfiguration
|
||||||
|
{
|
||||||
|
public string AppName { get; set; }
|
||||||
|
public string SeqUrl { get; set; }
|
||||||
|
public string SeqApiKey { get; set; }
|
||||||
|
public string AppBasePath { get; set; }
|
||||||
|
public string QCRoles { get; set; }
|
||||||
|
public string ITRoles { get; set; }
|
||||||
|
public string DocumentManagerApiKey { get; set; }
|
||||||
|
public string UnknownEnabledStatuses { get; set; }
|
||||||
|
public DefaultFilters DefaultFilters { get; set; } = new();
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class CoreApiWrapperConfiguration
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string GetPersonEndpoint { get; set; }
|
||||||
|
public string GetPersonsByNameLike { get; set; }
|
||||||
|
public string GetCountPersonsByNameLike { get; set; }
|
||||||
|
public string GetOrganizationEndpoint { get; set; }
|
||||||
|
public string GetOrganizationByNameEndpoint { get; set; }
|
||||||
|
public string GetOwnersByAccountNumber { get; set; }
|
||||||
|
public string CountOrganizationNumbersByName { get; set; }
|
||||||
|
public string GetPersonOrOrganizationNumberByMemberId { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetPersonEndpointUrl => Url + GetPersonEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetOrganizationEndpointUrl => Url + GetOrganizationEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetOrganizationByNameEndpointUrl => Url + GetOrganizationByNameEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetOwnersByAccountNumberUrl => Url + GetOwnersByAccountNumber;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string CountOrganizationNumbersByNameUrl => Url + CountOrganizationNumbersByName;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetPersonsByNameLikeUrl => Url + GetPersonsByNameLike;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetCountPersonsByNameLikeUrl => Url + GetCountPersonsByNameLike;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetPersonOrOrganizationNumberByMemberIdUrl => Url + GetPersonOrOrganizationNumberByMemberId;
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class DefaultFilters
|
||||||
|
{
|
||||||
|
public string QCStatusFilters { get; set; }
|
||||||
|
public string ITStatusFilters { get; set; }
|
||||||
|
public int CreationDateStartOffsetDays { get; set; }
|
||||||
|
public int CreationDateEndOffsetDays { get; set; }
|
||||||
|
public int IngestionDateStartOffsetDays { get; set; }
|
||||||
|
public int IngestionDateEndOffsetDays { get; set; }
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class DocumentIngestorApiConfiguration
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
public int QueueReprocessDocumentAllCriticalFailuresTimeoutSeconds { get; set; }
|
||||||
|
public int QueueReprocessCompletionAllNotificationDataFailuresTimeoutSeconds { get; set; }
|
||||||
|
public string FilteredDocumentProcessingInfoEndpoint { get; set; }
|
||||||
|
public string QueueReprocessDocumentByIdEndpoint { get; set; }
|
||||||
|
public string QueueReprocessDocumentWithOverrideByIdEndpoint { get; set; }
|
||||||
|
public string QueueReprocessDocumentAllCriticalFailuresEndpoint { get; set; }
|
||||||
|
public string SetDocumentsToEntityUnknownEndpoint { get; set; }
|
||||||
|
public string QueueReprocessCompletionAllNotificationDataFailures { get; set; }
|
||||||
|
public string UpdateStatusEndpoint { get; set; }
|
||||||
|
public string SetDocumentNotificationStatus { get; set; }
|
||||||
|
public string BulkSetDocumentNotificationStatus { get; set; }
|
||||||
|
public string GetDocumentProcessingInfoByDocumentId { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string FilteredDocumentProcessingInfoEndpointUrl => Url + FilteredDocumentProcessingInfoEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string QueueReprocessDocumentByIdEndpointUrl => Url + QueueReprocessDocumentByIdEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string QueueReprocessDocumentWithOverrideByIdEndpointUrl => Url + QueueReprocessDocumentWithOverrideByIdEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string QueueReprocessDocumentAllCriticalFailuresEndpointUrl => Url + QueueReprocessDocumentAllCriticalFailuresEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string SetDocumentsToEntityUnknownEndpointUrl => Url + SetDocumentsToEntityUnknownEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string QueueReprocessCompletionAllNotificationDataFailuresUrl => Url + QueueReprocessCompletionAllNotificationDataFailures;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string UpdateStatusEndpointUrl => Url + UpdateStatusEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string SetDocumentNotificationStatusUrl => Url + SetDocumentNotificationStatus;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string BulkSetDocumentNotificationStatusUrl => Url + BulkSetDocumentNotificationStatus;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string GetDocumentProcessingInfoByDocumentIdEndpointUrl => Url + GetDocumentProcessingInfoByDocumentId;
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class DocumentTypeRetrieverApiConfiguration
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
public string DocumentsRoute { get; set; }
|
||||||
|
public string CountDocumentsRoute { get; set; }
|
||||||
|
public string DocumentContentRoute { get; set; }
|
||||||
|
public string DocumentActionRoute { get; set; }
|
||||||
|
public string DocumentAuditRoute { get; set; }
|
||||||
|
public string DocCodeDetailsEndpoint { get; set; }
|
||||||
|
public string DocCodeDetailsAuditEndpoint { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string Documents => Url + DocumentsRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string CountDocuments => Url + CountDocumentsRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DocumentContent => Url + DocumentContentRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DocumentAction => Url + DocumentActionRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DocumentAudit => Url + DocumentAuditRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DocCodeDetailsEndpointUrl => Url + DocCodeDetailsEndpoint;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DocCodeDetailsAuditEndpointUrl => Url + DocCodeDetailsAuditEndpoint;
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class DocumentTypeUploaderApiConfiguration
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
public string AccountDocumentMetadataUpdateRoute { get; set; }
|
||||||
|
public string PrimaryOwnerDocumentMetadataUpdateRoute { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string AccountDocumentMetadataUpdate => Url + AccountDocumentMetadataUpdateRoute;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string PrimaryOwnerDocumentMetadataUpdate => Url + PrimaryOwnerDocumentMetadataUpdateRoute;
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public class FileOperationsApiConfiguration
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string GetPdfContentEndpoint { get; set; }
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
|
||||||
|
public record Security(AccessPolicy[] Policies);
|
||||||
|
public record AccessPolicy(string Name, string[] Groups, AccessPolicy[]? Policies);
|
||||||
|
|
||||||
|
public static class SecurityConfigurationFactory
|
||||||
|
{
|
||||||
|
public static Security FromConfiguration(IConfigurationSection configuration)
|
||||||
|
{
|
||||||
|
var policies = configuration
|
||||||
|
.GetSection("AccessPolicies")
|
||||||
|
.GetChildren()
|
||||||
|
.Select(PolicyFactory);
|
||||||
|
|
||||||
|
return new Security(policies.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AccessPolicy PolicyFactory(IConfigurationSection configuration)
|
||||||
|
{
|
||||||
|
return new AccessPolicy
|
||||||
|
(
|
||||||
|
configuration.Key,
|
||||||
|
configuration.GetValue<string>(nameof(AccessPolicy.Groups)).Split(",").Select(g => g.Trim()).ToArray(),
|
||||||
|
configuration.GetChildren().Where(c => c[nameof(AccessPolicy.Groups)] is not null).Select(PolicyFactory).ToArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2693
File diff suppressed because it is too large
Load Diff
+11
@@ -0,0 +1,11 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Enums;
|
||||||
|
|
||||||
|
public enum OwnershipTypes
|
||||||
|
{
|
||||||
|
MemberNumber,
|
||||||
|
AccountNumber,
|
||||||
|
PersonName,
|
||||||
|
OrganizationName,
|
||||||
|
OrganizationNumber,
|
||||||
|
PersonNumber
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<TypeScriptOutDir></TypeScriptOutDir>
|
||||||
|
<TypeScriptOutFile>wwwroot/scripts/app.js</TypeScriptOutFile>
|
||||||
|
<TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
|
||||||
|
<TypeScriptTarget>ES2020</TypeScriptTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<TypeScriptOutDir></TypeScriptOutDir>
|
||||||
|
<TypeScriptOutFile>wwwroot/scripts/app.js</TypeScriptOutFile>
|
||||||
|
<TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
|
||||||
|
<TypeScriptTarget>ES2020</TypeScriptTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>aspnet-LCU.WebApps.Internal.DocumentManager-6725618A-CB98-4C5B-9961-C82B04251A10</UserSecretsId>
|
||||||
|
<WebProject_DirectoryAccessLevelKey>0</WebProject_DirectoryAccessLevelKey>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<TypeScriptModuleKind>none</TypeScriptModuleKind>
|
||||||
|
<TypeScriptAllowJS>true</TypeScriptAllowJS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Blazored.FluentValidation" Version="2.1.0" />
|
||||||
|
<PackageReference Include="Blazored.Modal" Version="7.1.0" />
|
||||||
|
<PackageReference Include="Blazored.Toast" Version="3.2.2" />
|
||||||
|
<PackageReference Include="LCU.Library.DocumentType" Version="1.12.5" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.CoreApiWrapperModel" Version="2.27.3" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.DocumentIngestorModels" Version="1.20.1" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.DocumentModels" Version="1.8.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.FileOperationsModels" Version="4.1.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.LcuHttpClient" Version="2.0.1" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Logging" Version="1.1.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.RazorComponents" Version="2.3.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Security" Version="2.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Identity.Web" Version="3.11.0" />
|
||||||
|
<PackageReference Include="Microsoft.Identity.Web.UI" Version="3.11.0" />
|
||||||
|
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.8.3">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.18.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Diagnostics.FileVersionInfo" Version="4.3.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Configuration" Version="1.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<TypeScriptCompile Include="Shared\JSInterop\*.ts" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Shared\DocumentGridView\DocumentAttributes.razor.css" />
|
||||||
|
<None Remove="Shared\JSInterop\JSTooltips.js" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Shared\DocumentGridView\DocumentAttributes.razor.css" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="wwwroot\scripts\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<TypeScriptCompile Include="Shared\JSInterop\JSTooltips.js" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<OpenApiReference Include="DocumentTypeRetriever/spec.json" SourceUrl="https://qa-apps.landmarkcu.com/documenttyperetriever/swagger/v1/swagger.json">
|
||||||
|
<Namespace>DocumentTypeRetriever</Namespace>
|
||||||
|
<ClassName>Client</ClassName>
|
||||||
|
<OutputPath>DocumentTypeRetriever.Client.g.cs</OutputPath>
|
||||||
|
<Options>/DateTimeType:System.DateTime /GenerateClientInterfaces:true</Options>
|
||||||
|
</OpenApiReference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
using LCU.WebApps.Internal.DocumentManager.Pages;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Models.Constants;
|
||||||
|
|
||||||
|
public static class PolicyConstants
|
||||||
|
{
|
||||||
|
public static readonly List<string> Policies = new()
|
||||||
|
{
|
||||||
|
Admin,
|
||||||
|
DocumentLookup,
|
||||||
|
DocumentLookupDownload,
|
||||||
|
DocumentLookupEditVisibility,
|
||||||
|
DocumentLookupShowDocumentId,
|
||||||
|
DocCodeDetails,
|
||||||
|
DocCodeDetailsCreate,
|
||||||
|
DocCodeDetailsEdit,
|
||||||
|
ReprocessingTable,
|
||||||
|
ReprocessingTableAssignPrimaryOwner,
|
||||||
|
ReprocessingTableOpenDocument,
|
||||||
|
ITReprocessingTable,
|
||||||
|
ITReprocessingTableReprocess
|
||||||
|
};
|
||||||
|
|
||||||
|
public const string Admin = "Admin";
|
||||||
|
public const string DocumentLookup = "DocumentLookup";
|
||||||
|
public const string DocumentLookupDownload = "DocumentLookupDownload";
|
||||||
|
public const string DocumentLookupEditVisibility = "DocumentLookupEditVisibility";
|
||||||
|
public const string DocumentLookupShowDocumentId = "DocumentLookupShowDocumentId";
|
||||||
|
public const string DocCodeDetails = "DocCodeDetails";
|
||||||
|
public const string DocCodeDetailsCreate = "DocCodeDetailsCreate";
|
||||||
|
public const string DocCodeDetailsEdit = "DocCodeDetailsEdit";
|
||||||
|
public const string ReprocessingTable = "ReprocessingTable";
|
||||||
|
public const string ReprocessingTableAssignPrimaryOwner = "ReprocessingTableAssignPrimaryOwner";
|
||||||
|
public const string ReprocessingTableOpenDocument = "ReprocessingTableOpenDocument";
|
||||||
|
public const string ITReprocessingTable = "ITReprocessingTable";
|
||||||
|
public const string ITReprocessingTableReprocess = "ITReprocessingTableReprocess";
|
||||||
|
}
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
using DocumentTypeRetriever;
|
||||||
|
using DocumentMetadataResponse = LCU.Library.DocumentType.Models.Responses.External.DocumentMetadataResponse;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Models;
|
||||||
|
|
||||||
|
public static class DocumentCodeDetailsExcludeMemberViewExtension
|
||||||
|
{
|
||||||
|
public static string ExcludeMemberView(this DocumentCodeDetailsResponse documentCodeDetail)
|
||||||
|
{
|
||||||
|
const string dateFormat = "MM/dd/yyyy";
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberView)
|
||||||
|
{
|
||||||
|
return "All";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is null && documentCodeDetail.DisableMemberViewEndDate is null)
|
||||||
|
{
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is null)
|
||||||
|
{
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewEndDate is null)
|
||||||
|
{
|
||||||
|
return $"{documentCodeDetail.DisableMemberViewStartDate.Value.ToString(dateFormat)} - Forever";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is not null && documentCodeDetail.DisableMemberViewEndDate is not null)
|
||||||
|
{
|
||||||
|
return $"{documentCodeDetail.DisableMemberViewStartDate.Value.ToString(dateFormat)} - {documentCodeDetail.DisableMemberViewEndDate.Value.ToString(dateFormat)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDisabledInMemberView(this DocumentCodeDetailsResponse documentCodeDetail,
|
||||||
|
DocumentMetadataResponse document)
|
||||||
|
{
|
||||||
|
if (documentCodeDetail.DisableMemberView)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is null && documentCodeDetail.DisableMemberViewEndDate is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewEndDate is null)
|
||||||
|
{
|
||||||
|
return documentCodeDetail.DisableMemberViewStartDate.Value.ToUniversalTime() <= document.CreatedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentCodeDetail.DisableMemberViewStartDate is not null && documentCodeDetail.DisableMemberViewEndDate is not null)
|
||||||
|
{
|
||||||
|
return documentCodeDetail.DisableMemberViewStartDate.Value.ToUniversalTime() <= document.CreatedDate
|
||||||
|
&& documentCodeDetail.DisableMemberViewEndDate.Value.ToUniversalTime() >= document.CreatedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Models;
|
||||||
|
|
||||||
|
public class OverrideState
|
||||||
|
{
|
||||||
|
public OverrideState(int documentProcessingInfoId)
|
||||||
|
{
|
||||||
|
SelectedEntityType = "";
|
||||||
|
DnaResults = "";
|
||||||
|
OwnerOverrideInfo = new OwnerOverrideInfo
|
||||||
|
{
|
||||||
|
DocumentProcessingInfoId = documentProcessingInfoId,
|
||||||
|
EntityId = ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public OwnerOverrideInfo OwnerOverrideInfo { get; set; }
|
||||||
|
public string SelectedEntityType { get; set; }
|
||||||
|
public string DnaResults { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsEntityIdDisabled => string.IsNullOrWhiteSpace(SelectedEntityType) ||
|
||||||
|
SelectedEntityType == @Enum.GetName(typeof(EntityTypes), EntityTypes.Unknown);
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsDnaLookUpDisabled => string.IsNullOrWhiteSpace(OwnerOverrideInfo.EntityId);
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool PersonOrganizationAndEntityIdFilled => !string.IsNullOrWhiteSpace(OwnerOverrideInfo.EntityId) &&
|
||||||
|
(OwnerOverrideInfo.EntityType == EntityTypes.Person ||
|
||||||
|
OwnerOverrideInfo.EntityType == EntityTypes.Organization);
|
||||||
|
[NotMapped]
|
||||||
|
public bool HasDnaResult => !string.IsNullOrWhiteSpace(DnaResults);
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsEntityTypeEmpty => !string.IsNullOrWhiteSpace(SelectedEntityType);
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool ValuesSetForOverrideAndReprocess
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var canOverrideAndReprocess = false;
|
||||||
|
if (IsEntityTypeEmpty)
|
||||||
|
{
|
||||||
|
OwnerOverrideInfo.EntityType = GetEntityTypes(SelectedEntityType);
|
||||||
|
canOverrideAndReprocess = (HasDnaResult && PersonOrganizationAndEntityIdFilled) ||
|
||||||
|
OwnerOverrideInfo.EntityType == EntityTypes.Unknown;
|
||||||
|
}
|
||||||
|
return canOverrideAndReprocess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityTypes GetEntityTypes(string entityTypeString)
|
||||||
|
{
|
||||||
|
return entityTypeString switch
|
||||||
|
{
|
||||||
|
nameof(EntityTypes.Unknown) => EntityTypes.Unknown,
|
||||||
|
nameof(EntityTypes.Organization) => EntityTypes.Organization,
|
||||||
|
_ => EntityTypes.Person
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
@page "/DocCodeDetails"
|
||||||
|
@using DocumentTypeRetriever;
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@attribute [Authorize(PolicyConstants.DocCodeDetails)]
|
||||||
|
@inject IDocumentCodeDetailsTableViewModel ViewModel;
|
||||||
|
@inject AuthenticationHelper AuthenticationHelper;
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h4>Doc Code Details Table</h4>
|
||||||
|
</div>
|
||||||
|
<TableFilter ViewModel="@ViewModel">
|
||||||
|
<FilterControls>
|
||||||
|
<DocumentCodeDetailsFilterControls Filter="@ViewModel.Filter"/>
|
||||||
|
</FilterControls>
|
||||||
|
<SortControls>
|
||||||
|
<TableSort ViewModel="@ViewModel"/>
|
||||||
|
</SortControls>
|
||||||
|
<AdditionalControls>
|
||||||
|
<DocCodeDetailsCreateButton CreateDocumentCodeDetails="@ViewModel.CreateDocumentCodeDetails"/>
|
||||||
|
</AdditionalControls>
|
||||||
|
</TableFilter>
|
||||||
|
@if (ViewModel.Loaded)
|
||||||
|
{
|
||||||
|
<DataTable ViewModel=@ViewModel T="DocumentCodeDetails " U="DocumentCodeDetailsResponse ">
|
||||||
|
<Row>
|
||||||
|
<DocumentCodeDetailsHeaderRow DocumentCodeDetail="@context.Details" />
|
||||||
|
</Row>
|
||||||
|
<RowContent>
|
||||||
|
<DocumentCodeDetailsRowBody
|
||||||
|
DocumentCodeDetail="@context.data.Details"
|
||||||
|
DocumentCodeDetailEdit="@context.pendingEdit"
|
||||||
|
SavePendingEdit="@context.savePendingEdit"
|
||||||
|
RefreshCodeDetails="@ViewModel.Refresh"
|
||||||
|
SetCollapsed="@context.setCollapsed"
|
||||||
|
EditingAllowed="@ViewModel.IsUserAuthorizedToEdit"
|
||||||
|
/>
|
||||||
|
<DocumentCodeDetailsAuditLog AuditTrail="@context.data.Logs"/>
|
||||||
|
</RowContent>
|
||||||
|
</DataTable>
|
||||||
|
<TablePaging ViewModel="@ViewModel"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<CardDisplay>
|
||||||
|
<div class="p-5">
|
||||||
|
<h3>No document data has been loaded...</h3>
|
||||||
|
</div>
|
||||||
|
</CardDisplay>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
protected override async void OnInitialized()
|
||||||
|
{
|
||||||
|
await AuthenticationHelper.GetServiceToken(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
|
||||||
|
ViewModel.VisibleItems.CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ViewModel.VisibleItems.Count == ViewModel.VisibleItemsCount)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if(firstRender) await ViewModel.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
@page
|
||||||
|
@model LCU.WebApps.Internal.DocumentManager.Pages.ErrorModel
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<title>Error</title>
|
||||||
|
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
|
||||||
|
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="main">
|
||||||
|
<div class="content px-4">
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
@if (Model.ShowRequestId)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
|
and restarting the app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Pages
|
||||||
|
{
|
||||||
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
|
[IgnoreAntiforgeryToken]
|
||||||
|
public class ErrorModel : PageModel
|
||||||
|
{
|
||||||
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
|
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
|
private readonly ILogger<ErrorModel> _logger;
|
||||||
|
|
||||||
|
public ErrorModel(ILogger<ErrorModel> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGet()
|
||||||
|
{
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
@page "/ITReprocessingTable"
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@attribute [Authorize(PolicyConstants.ITReprocessingTable)]
|
||||||
|
@inject ITablePagingViewModel<DocumentProcessingInfo, Filter> ViewModel;
|
||||||
|
@inject AuthenticationHelper AuthenticationHelper;
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h4>IT Reprocessing Table</h4>
|
||||||
|
</div>
|
||||||
|
<TableFilter ViewModel="@ViewModel">
|
||||||
|
<FilterControls>
|
||||||
|
<DocumentProcessingInfoFilterControls Filter="@ViewModel.Filter">
|
||||||
|
<AdditionalFilters Context="filter">
|
||||||
|
<IngestorRunInfoIdFilterComponent Filter="@filter"/>
|
||||||
|
<CustomIndexFileFilter Filter="@filter"
|
||||||
|
Validate="@Validate"/>
|
||||||
|
<div class="form-row col-12 my-3">
|
||||||
|
<div class="col-6">
|
||||||
|
<DocumentProcessingInfoProcessStatusFilter Filter="@filter"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<DocumentProcessingInfoNotificationStatusFilter Filter="@filter"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdditionalFilters>
|
||||||
|
</DocumentProcessingInfoFilterControls>
|
||||||
|
<FluentValidationValidator @ref="_fluentValidationValidator" Options="@(options => options.IncludeRuleSets(_ruleSets))"/>
|
||||||
|
</FilterControls>
|
||||||
|
<SortControls>
|
||||||
|
<TableSort ViewModel="@ViewModel"/>
|
||||||
|
</SortControls>
|
||||||
|
<AdditionalControls>
|
||||||
|
<div class="row justify-content-end">
|
||||||
|
<div class="mr-3">
|
||||||
|
<SetNotificationStatusButton Filter="@ViewModel.Filter"
|
||||||
|
RefreshData="@RefreshDataAndCloseModal"/>
|
||||||
|
</div>
|
||||||
|
<div class="mr-3">
|
||||||
|
<ReprocessNotificationDataFailuresButton/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ReprocessCriticalFailuresButton/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdditionalControls>
|
||||||
|
</TableFilter>
|
||||||
|
@if (ViewModel.Loaded && ViewModel.VisibleItemsCount > 0)
|
||||||
|
{
|
||||||
|
<DataTable ViewModel=@ViewModel T="DocumentProcessingInfo " U="OverrideState ">
|
||||||
|
<Row>
|
||||||
|
<DocumentProcessingHeaderRow DocumentProcessingInfo="@context">
|
||||||
|
<AdditionalRowContent>
|
||||||
|
<NotificationStatusColumn DocumentProcessingInfo="@context"/>
|
||||||
|
</AdditionalRowContent>
|
||||||
|
</DocumentProcessingHeaderRow>
|
||||||
|
</Row>
|
||||||
|
<RowContent>
|
||||||
|
<ITReprocessingRowBody
|
||||||
|
DocumentProcessingInfo="@context.data"
|
||||||
|
OverrideState="@context.pendingEdit"
|
||||||
|
SavePendingEdit="@context.savePendingEdit"
|
||||||
|
RefreshData="@RefreshDataAndCloseModal"
|
||||||
|
SetCollapsed="@context.setCollapsed">
|
||||||
|
<AdditionalRowContent>
|
||||||
|
<NotificationChangeStatusContainer
|
||||||
|
DocumentProcessingInfo="@context.data"
|
||||||
|
RefreshData="@RefreshDataAndCloseModal"/>
|
||||||
|
<ChangeStatusContainer
|
||||||
|
DocumentProcessingInfo="@context.data"
|
||||||
|
RefreshData="@RefreshDataAndCloseModal"/>
|
||||||
|
</AdditionalRowContent>
|
||||||
|
</ITReprocessingRowBody>
|
||||||
|
</RowContent>
|
||||||
|
</DataTable>
|
||||||
|
<TablePaging ViewModel="@ViewModel"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<CardDisplay>
|
||||||
|
<div class="p-5">
|
||||||
|
<h3>
|
||||||
|
<span>No document data has been loaded</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</CardDisplay>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private FluentValidationValidator? _fluentValidationValidator;
|
||||||
|
private readonly string[] _ruleSets = {"CustomIndexFile"};
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await AuthenticationHelper.GetServiceToken(DownstreamWebService.DocumentIngestor);
|
||||||
|
|
||||||
|
ViewModel.VisibleItems.CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ViewModel.VisibleItems.Count == ViewModel.VisibleItemsCount)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) await ViewModel.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshDataAndCloseModal(IModalReference openModal)
|
||||||
|
{
|
||||||
|
ViewModel.Refresh();
|
||||||
|
openModal.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Validate()
|
||||||
|
{
|
||||||
|
ViewModel.FilterIsValid = await _fluentValidationValidator!.ValidateAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
@page "/"
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@attribute [Authorize(PolicyConstants.DocumentLookup)]
|
||||||
|
@inject IBusinessViewViewModel ViewModel;
|
||||||
|
@inject AuthenticationHelper AuthenticationHelper;
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h4>Document Lookup</h4>
|
||||||
|
</div>
|
||||||
|
<TableFilter ViewModel="@ViewModel">
|
||||||
|
<FilterControls>
|
||||||
|
<BusinessViewFilterControls
|
||||||
|
Filter="@ViewModel.Filter"
|
||||||
|
OnOwnershipChange="@(change => ViewModel.FilterChanged(change.type, nameof(BusinessViewFilter.OwnershipType), change.value))"
|
||||||
|
OnAdditionalOwnershipChange="@(change => ViewModel.FilterChanged(change.type, nameof(BusinessViewFilter.AdditionalOwnershipValue), change.value))"/>
|
||||||
|
</FilterControls>
|
||||||
|
<SortControls>
|
||||||
|
<TableSort ViewModel="@ViewModel"/>
|
||||||
|
</SortControls>
|
||||||
|
</TableFilter>
|
||||||
|
@if (ViewModel.Loaded && !_dataLoadError && ViewModel.VisibleItemsCount > 0)
|
||||||
|
{
|
||||||
|
<DataTable ViewModel=@ViewModel T="BusinessViewDocumentInformation " U="object ">
|
||||||
|
<Row>
|
||||||
|
<BusinessViewDocumentHeaderRow Document="@context.DocumentInformation"/>
|
||||||
|
</Row>
|
||||||
|
<RowContent>
|
||||||
|
<BusinessViewDocumentRowBody Document="@context.data.DocumentInformation" ViewModel="@ViewModel"/>
|
||||||
|
<BusinessViewDocumentAuditTable AuditTrail="@context.data.Logs.OrderByDescending(x => x.Date)"/>
|
||||||
|
</RowContent>
|
||||||
|
</DataTable>
|
||||||
|
<TablePaging ViewModel="@ViewModel"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<CardDisplay>
|
||||||
|
<div class="p-5">
|
||||||
|
<h3>
|
||||||
|
@if (!_dataLoadError)
|
||||||
|
{
|
||||||
|
<span>No document data has been loaded</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="text-danger">Something went wrong... Please check back later. If the problem persists contact IT.</span>
|
||||||
|
}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</CardDisplay>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool _dataLoadError = false;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await AuthenticationHelper.GetServiceToken(DownstreamWebService.DocumentTypeUploader);
|
||||||
|
await AuthenticationHelper.GetServiceToken(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
|
||||||
|
ViewModel.VisibleItems.CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ViewModel.VisibleItems.Count == ViewModel.VisibleItemsCount || ViewModel.VisibleItemsCount == 0)
|
||||||
|
{
|
||||||
|
_dataLoadError = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewModel.DataLoadError += delegate
|
||||||
|
{
|
||||||
|
_dataLoadError = true;
|
||||||
|
StateHasChanged();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) await ViewModel.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void RefreshDataAndCloseModal(IModalReference openModal)
|
||||||
|
{
|
||||||
|
ViewModel.Refresh();
|
||||||
|
openModal.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
@page "/ReprocessingTable"
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@attribute [Authorize(PolicyConstants.ReprocessingTable)]
|
||||||
|
@inject ITablePagingViewModel<DocumentProcessingInfo, Filter> ViewModel;
|
||||||
|
@inject AuthenticationHelper AuthenticationHelper;
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h4>Reprocessing Table</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TableFilter ViewModel="@ViewModel">
|
||||||
|
<FilterControls>
|
||||||
|
<DocumentProcessingInfoFilterControls Filter="@ViewModel.Filter"
|
||||||
|
Validate="@Validate">
|
||||||
|
<AdditionalFilters Context="filter">
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-12">
|
||||||
|
<DocumentProcessingInfoProcessStatusFilter Filter="@filter"
|
||||||
|
Validate="@Validate"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<DocumentProcessingInfoSearchCriteriaFilter Filter="@filter"
|
||||||
|
Validate="@Validate"/>
|
||||||
|
</div>
|
||||||
|
</AdditionalFilters>
|
||||||
|
</DocumentProcessingInfoFilterControls>
|
||||||
|
<FluentValidationValidator @ref="_fluentValidationValidator" Options="@(options => options.IncludeRuleSets(_ruleSets))"/>
|
||||||
|
</FilterControls>
|
||||||
|
<SortControls>
|
||||||
|
<TableSort ViewModel="@ViewModel"/>
|
||||||
|
</SortControls>
|
||||||
|
<AdditionalControls>
|
||||||
|
<div class="row justify-content-end">
|
||||||
|
<div>
|
||||||
|
<SetFilteredDocumentsEntityToUnknownButton ViewModel="@ViewModel"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdditionalControls>
|
||||||
|
</TableFilter>
|
||||||
|
|
||||||
|
@if (ViewModel.Loaded && ViewModel.VisibleItemsCount > 0)
|
||||||
|
{
|
||||||
|
<DataTable ViewModel=@ViewModel T="DocumentProcessingInfo " U="OverrideState ">
|
||||||
|
<Row>
|
||||||
|
<DocumentProcessingHeaderRow DocumentProcessingInfo="@context"/>
|
||||||
|
</Row>
|
||||||
|
<RowContent>
|
||||||
|
<BusinessReprocessingRowBody
|
||||||
|
DocumentProcessingInfo="@context.data"
|
||||||
|
OverrideState="@context.pendingEdit"
|
||||||
|
SavePendingEdit="@context.savePendingEdit"
|
||||||
|
RefreshData="@RefreshDataAndCloseModal"
|
||||||
|
SetCollapsed="@context.setCollapsed"/>
|
||||||
|
</RowContent>
|
||||||
|
</DataTable>
|
||||||
|
<TablePaging ViewModel="@ViewModel"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<CardDisplay>
|
||||||
|
<div class="p-5">
|
||||||
|
<h3>
|
||||||
|
<span>No document data has been loaded</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</CardDisplay>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
private FluentValidationValidator? _fluentValidationValidator;
|
||||||
|
private readonly string[] _ruleSets = {"SearchCriteria"};
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await AuthenticationHelper.GetServiceToken(DownstreamWebService.DocumentIngestor);
|
||||||
|
|
||||||
|
ViewModel.VisibleItems.CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ViewModel.VisibleItems.Count == ViewModel.VisibleItemsCount || ViewModel.VisibleItemsCount == 0)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) await ViewModel.Refresh();
|
||||||
|
await Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RefreshDataAndCloseModal(IModalReference openModal)
|
||||||
|
{
|
||||||
|
await ViewModel.Refresh();
|
||||||
|
openModal.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Validate()
|
||||||
|
{
|
||||||
|
ViewModel.FilterIsValid = await _fluentValidationValidator!.ValidateAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
@page "/"
|
||||||
|
@namespace LCU.WebApps.Internal.DocumentManager.Pages
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
||||||
|
<base href="~/" />
|
||||||
|
|
||||||
|
<component type="typeof(App)" render-mode="ServerPrerendered" />
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Configurations
|
||||||
|
@namespace LCU.WebApps.Internal.DocumentManager.Pages
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@inject AppConfiguration _appConfiguration;
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="~/@_appConfiguration.AppBasePath" />
|
||||||
|
|
||||||
|
<link href="css/site.css" rel="stylesheet" />
|
||||||
|
<link href="LCU.WebApps.Internal.DocumentManager.styles.css" rel="stylesheet" />
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||||
|
<link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" />
|
||||||
|
<link href="_content/LCU.Library.Internal.RazorComponents/styles.css" rel="stylesheet" />
|
||||||
|
<link href="_content/LCU.Library.Internal.RazorComponents/bootstrap.min.css" rel="stylesheet" />
|
||||||
|
<link href="_content/Blazored.Modal/blazored-modal.css" rel="stylesheet" />
|
||||||
|
<link href="_content/Blazored.Typeahead/blazored-typeahead.css" rel="stylesheet" />
|
||||||
|
<link href="_content/LCU.Library.Internal.RazorComponents/grid.css" rel="stylesheet" />
|
||||||
|
<link href="_content/LCU.Library.Internal.RazorComponents/images/favicon.ico" rel="icon" type="image/x-icon" />
|
||||||
|
<link href="_content/LCU.Library.Internal.RazorComponents/open-iconic/font/css/open-iconic-bootstrap.min.css" rel="stylesheet" />
|
||||||
|
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@RenderBody()
|
||||||
|
|
||||||
|
<div id="blazor-error-ui">
|
||||||
|
<environment include="Staging,Production">
|
||||||
|
An error has occurred. This application may no longer respond until reloaded.
|
||||||
|
</environment>
|
||||||
|
<environment include="Development">
|
||||||
|
An unhandled exception has occurred. See browser dev tools for details.
|
||||||
|
</environment>
|
||||||
|
<a href="" class="reload">Reload</a>
|
||||||
|
<a class="dismiss">🗙</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="_framework/blazor.server.js"></script>
|
||||||
|
<script src="_content/Blazored.Modal/blazored.modal.js"></script>
|
||||||
|
<script src="_content/Blazored.Typeahead/blazored-typeahead.js"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
<script src="scripts/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||||
|
using Microsoft.Identity.Web;
|
||||||
|
using Microsoft.Identity.Web.UI;
|
||||||
|
using Serilog;
|
||||||
|
using Blazored.Modal;
|
||||||
|
using Blazored.Toast;
|
||||||
|
using Blazored.Toast.Services;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination;
|
||||||
|
using LCU.Library.Internal.Logging;
|
||||||
|
using LCU.Library.Internal.Logging.Models;
|
||||||
|
using LCU.Library.Internal.Configuration;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.JSInterop;
|
||||||
|
using LCU.Library.Internal.Security;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient;
|
||||||
|
|
||||||
|
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/document-manager-seq-api-key.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/document-manager-client-secret.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/document-manager-client-id.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/document-manager-api-key.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Host.UseSerilog();
|
||||||
|
if (builder.Environment.EnvironmentName.Contains("Local", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.WebHost.UseStaticWebAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildServicesAndConfigurations(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var services = builder.Services;
|
||||||
|
var configuration = builder.Configuration;
|
||||||
|
|
||||||
|
services.AddRazorPages();
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
services.AddAzureAdGroupPolicyServicesWithToken(configuration.GetSection("AzureAd"),
|
||||||
|
configuration.GetSection("Security"));
|
||||||
|
services.AddControllersWithViews().AddMicrosoftIdentityUI();
|
||||||
|
services.AddServerSideBlazor().AddMicrosoftIdentityConsentHandler();
|
||||||
|
services.AddHttpClient();
|
||||||
|
services.AddLCUHttpServices();
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
|
var appConfiguration = configuration.Bind<AppConfiguration>(services, "Primary");
|
||||||
|
configuration.Bind<CoreApiWrapperConfiguration>(services);
|
||||||
|
configuration.Bind<DocumentIngestorApiConfiguration>(services);
|
||||||
|
configuration.Bind<FileOperationsApiConfiguration>(services);
|
||||||
|
var documentTypeRetrieverConfiguration = configuration.Bind<DocumentTypeRetrieverApiConfiguration>(services);
|
||||||
|
configuration.Bind<DocumentTypeUploaderApiConfiguration>(services);
|
||||||
|
|
||||||
|
var loggerConfiguration = new LoggingConfiguration(appConfiguration.AppName,
|
||||||
|
appConfiguration.SeqApiKey,
|
||||||
|
appConfiguration.SeqUrl);
|
||||||
|
var lcuLogging = new LcuLogging(loggerConfiguration);
|
||||||
|
lcuLogging.UseLcuLogging();
|
||||||
|
|
||||||
|
AddDocumentManagerServices(services);
|
||||||
|
AddBlazoreAdditionServices(services);
|
||||||
|
AddJavascriptInterop(services);
|
||||||
|
services.AddScoped<AuthenticationHelper>();
|
||||||
|
services.AddTransient<IDocumentCodeDetailsTableViewModel, DocumentCodeDetailsTableViewModel>();
|
||||||
|
services.AddTransient<ITablePagingViewModel<DocumentProcessingInfo, Filter>, DocumentProcessingInfoTableViewModel>();
|
||||||
|
services.AddTransient<IBusinessViewViewModel, BusinessViewViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDocumentManagerServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<DocumentProcessingInfoService>();
|
||||||
|
services.AddScoped<IDocumentProcessingInfoService>(p => new DocumentProcessingInfoServiceErrorDecorator(p.GetRequiredService<DocumentProcessingInfoService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
|
||||||
|
services.AddScoped<QueueDocumentReprocessingService>();
|
||||||
|
services.AddScoped<IDocumentReprocessingService>(p => new DocumentReprocessingServiceErrorDecorator(p.GetRequiredService<QueueDocumentReprocessingService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
|
||||||
|
services.AddScoped<CoreApiWrapperService>();
|
||||||
|
services.AddScoped<IEntityService>(p => new EntityServiceErrorDecorator(p.GetRequiredService<CoreApiWrapperService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
|
||||||
|
services.AddScoped<DocumentTypeRetrieverService>();
|
||||||
|
services.AddScoped<IDocumentService>(p => new DocumentServiceErrorDecorator(p.GetRequiredService<DocumentTypeRetrieverService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
services.AddScoped<IDocumentTypeService>(p => new DocumentTypeServiceErrorDecorator(p.GetRequiredService<DocumentTypeRetrieverService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
|
||||||
|
services.AddScoped<DocumentTypeUploaderService>();
|
||||||
|
services.AddScoped<IDocumentTypeUploaderService>(p => new DocumentTypeUploaderServiceErrorDecorator(p.GetRequiredService<DocumentTypeUploaderService>(), p.GetRequiredService<IToastService>()));
|
||||||
|
|
||||||
|
services.AddScoped<IDocumentAuthorization, BusinessViewViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddBlazoreAdditionServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddBlazoredToast();
|
||||||
|
services.AddBlazoredModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddJavascriptInterop(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddTransient<IJSDate, JSDate>();
|
||||||
|
services.AddTransient<IJSDocument, JSDocument>();
|
||||||
|
services.AddTransient<IJSTooltips, JSTooltips>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildAppAndRun(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error");
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.MapControllers();
|
||||||
|
app.MapBlazorHub();
|
||||||
|
app.MapFallbackToPage("/_Host");
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:31489",
|
||||||
|
"sslPort": 44347
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"Local-QA": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Local-QA"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:7141;http://localhost:5141",
|
||||||
|
"dotnetRunMessages": true
|
||||||
|
},
|
||||||
|
"Local-Local": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Local-Local"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:7141;http://localhost:5141",
|
||||||
|
"dotnetRunMessages": true
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Accounts;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Mixed;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Organizations;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Persons;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class CoreApiWrapperService : IEntityService
|
||||||
|
{
|
||||||
|
private readonly IGetRequest _getRequest;
|
||||||
|
private readonly CoreApiWrapperConfiguration _coreApiWrapperConfiguration;
|
||||||
|
|
||||||
|
public CoreApiWrapperService(IGetRequest getRequest,
|
||||||
|
CoreApiWrapperConfiguration coreApiWrapperConfiguration)
|
||||||
|
{
|
||||||
|
_getRequest = getRequest;
|
||||||
|
_coreApiWrapperConfiguration = coreApiWrapperConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Person>> GetPersonByEntityNumber(string entityNumber)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _coreApiWrapperConfiguration.GetPersonEndpointUrl.Replace("{personNumber}", entityNumber);
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<List<Person>>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get Person By Entity Number. EntityNumber: {entityNumber}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Person>?> GetPersonsByNameLike(PersonNameRequest request, int limit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uriString = EncodePersonNameRequestUri(request, limit);
|
||||||
|
var url = $"{_coreApiWrapperConfiguration.GetPersonsByNameLikeUrl}{uriString}";
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<List<Person>>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get Persons By Name Like: PersonNameRequest. PersonNameRequest: {JsonConvert.SerializeObject(request)}. Limit: {limit}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> GetCountPersonsByNameLike(PersonNameRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uriString = EncodePersonNameRequestUri(request);
|
||||||
|
var url = $"{_coreApiWrapperConfiguration.GetCountPersonsByNameLikeUrl}{uriString}";
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<int>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get Count Persons By Name Like: PersonNameRequest: {JsonConvert.SerializeObject(request)}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Organization>> GetOrganizationByEntityNumber(string entityNumber)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _coreApiWrapperConfiguration.GetOrganizationEndpointUrl.Replace("{organizationNumber}", entityNumber);
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<List<Organization>>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get Organization By Entity Number. EntityNumber: {entityNumber}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Organization>?> GetOrganizationByOrganizationName(string organizationName, int limit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
query["limit"] = limit.ToString();
|
||||||
|
var url = _coreApiWrapperConfiguration.GetOrganizationByNameEndpointUrl.Replace("{organizationName}", organizationName) + $"?{query}";
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<List<Organization>>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get Organization By Organization Name. OrganizationName: {organizationName}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EncodePersonNameRequestUri(PersonNameRequest request, int? limit = null)
|
||||||
|
{
|
||||||
|
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.FirstName))
|
||||||
|
{
|
||||||
|
nameValueCollection.Add("FirstName", request.FirstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.MdlName))
|
||||||
|
{
|
||||||
|
nameValueCollection.Add("MdlName", request.MdlName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.LastName))
|
||||||
|
{
|
||||||
|
nameValueCollection.Add("LastName", request.LastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit != null)
|
||||||
|
{
|
||||||
|
nameValueCollection.Add("limit", limit.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"?{nameValueCollection}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<AccountOwnership>> GetOwnersByAccountNumber(long accountNumber, DateTime creationDate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _coreApiWrapperConfiguration.GetOwnersByAccountNumberUrl;
|
||||||
|
var route = new UriBuilder(url);
|
||||||
|
var query = HttpUtility.ParseQueryString("");
|
||||||
|
query["accountNumber"] = accountNumber.ToString();
|
||||||
|
query["creationDate"] = creationDate.ToString();
|
||||||
|
route.Query = query.ToString();
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<List<AccountOwnership>>(route.ToString());
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Get AccountOwnership by account number. AccountNumber: {accountNumber}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> CountOrganizationNumbersByName(string organizationName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _coreApiWrapperConfiguration.CountOrganizationNumbersByNameUrl.Replace("{organizationName}", organizationName);
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<long>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Counting Organization By Organization Name. OrganizationName: {organizationName}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PersonOrganization> GetPersonOrOrganizationNumberByMemberId(long memberId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _coreApiWrapperConfiguration.GetPersonOrOrganizationNumberByMemberIdUrl.Replace("{memberId}", memberId.ToString());
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<PersonOrganization>(url);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Error Getting Member by Id. MemberId: {memberId}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class DocumentProcessingInfoServiceErrorDecorator : ToastErrorServiceDecorator<IDocumentProcessingInfoService>, IDocumentProcessingInfoService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Document Ingestor";
|
||||||
|
|
||||||
|
public DocumentProcessingInfoServiceErrorDecorator(IDocumentProcessingInfoService instance, IToastService toastService)
|
||||||
|
: base(instance, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetDocumentsToEntityUnknownByFilter(Filter filter, int totalItems)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.SetDocumentsToEntityUnknownByFilter(filter, totalItems),
|
||||||
|
"There was an issue setting entities to Unknown. Re-apply the filters and/or refresh the page. If failure continues, submit a ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task SetDocumentToEntityUnknown(params int[] documentProcessingInfoIds)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.SetDocumentToEntityUnknown(documentProcessingInfoIds),
|
||||||
|
"Failure to set document entity to unknown. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task<PagedResponse<DocumentProcessingInfo>> GetFilteredDocumentProcessingInfos(Filter filter)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.GetFilteredDocumentProcessingInfos(filter),
|
||||||
|
"Failure to get Document Processing Information. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task<DocumentProcessingInfo> UpdateStatus(DocumentProcessingInfoStatusRequest request)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.UpdateStatus(request),
|
||||||
|
"Failure to Update Status. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task SetDocumentNotificationStatus(int documentProcessingInfoId, NotificationStatusTypes notificationStatus, string reason)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.SetDocumentNotificationStatus(documentProcessingInfoId, notificationStatus, reason),
|
||||||
|
"Failure to set notification status. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
public Task BulkSetDocumentNotificationStatus(Filter filter, NotificationStatusTypes status, string reason)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.BulkSetDocumentNotificationStatus(filter, status, reason),
|
||||||
|
"Failure to batch set notification status. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class DocumentReprocessingServiceErrorDecorator : ToastErrorServiceDecorator<IDocumentReprocessingService>, IDocumentReprocessingService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Document Ingestor";
|
||||||
|
|
||||||
|
public DocumentReprocessingServiceErrorDecorator(IDocumentReprocessingService instance, IToastService toastService)
|
||||||
|
: base(instance, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ReprocessAllNotificationDataFailures()
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.ReprocessAllNotificationDataFailures(),
|
||||||
|
$"There was an issue while queueing {nameof(ProcessStatusTypes.NotificationDataFailure)} documents. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task QueueDocumentReprocessingWithOverride(OwnerOverrideInfo ownerOverrideInfo)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.QueueDocumentReprocessingWithOverride(ownerOverrideInfo),
|
||||||
|
"Failure to queue document for reprocessing. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task QueueDocumentReprocessing(int documentProcessingInfoId)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.QueueDocumentReprocessing(documentProcessingInfoId),
|
||||||
|
"Failure to queue document for reprocessing. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
|
||||||
|
public Task QueueDocumentReprocessingAllCriticalFailures()
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.QueueDocumentReprocessingAllCriticalFailures(),
|
||||||
|
"There was an issue while queueing CriticalFailure documents. Please refresh page and if failure continues submit ticket."
|
||||||
|
);
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using DocumentTypeRetriever;
|
||||||
|
using DocumentAction = LCU.Library.Internal.DocumentModels.Requests.DocumentAction;
|
||||||
|
using DocumentAuditResponse = LCU.Library.Internal.DocumentModels.Responses.DocumentAuditResponse;
|
||||||
|
using DocumentMetadataResponse = LCU.Library.DocumentType.Models.Responses.External.DocumentMetadataResponse;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class DocumentServiceErrorDecorator : ToastErrorServiceDecorator<IDocumentService>, IDocumentService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Document API";
|
||||||
|
|
||||||
|
public DocumentServiceErrorDecorator(IDocumentService instance, IToastService toastService)
|
||||||
|
: base(instance, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IEnumerable<DocumentMetadataResponse>> GetDocuments(DocumentInformationFilteredSearch filter)
|
||||||
|
=> TryAndToast(() => Instance.GetDocuments(filter));
|
||||||
|
public Task<long> CountDocuments(DocumentInformationFilter filter)
|
||||||
|
=> TryAndToast(() => Instance.CountDocuments(filter));
|
||||||
|
public Task<string> StreamDocumentContent(Guid documentBlobId)
|
||||||
|
=> TryAndToast(() => Instance.StreamDocumentContent(documentBlobId));
|
||||||
|
public Task ExternalActionPerformed(Guid documentId, DocumentAction action, string source)
|
||||||
|
=> TryAndToast(() => Instance.ExternalActionPerformed(documentId, action, source));
|
||||||
|
public Task<IEnumerable<DocumentAuditResponse>> GetDocumentLogs(Guid[] documentIds)
|
||||||
|
=> TryAndToast(() => Instance.GetDocumentLogs(documentIds));
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using DocumentTypeRetriever;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class DocumentTypeServiceErrorDecorator : ToastErrorServiceDecorator<IDocumentTypeService>, IDocumentTypeService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Document Type Retriever";
|
||||||
|
|
||||||
|
public DocumentTypeServiceErrorDecorator(IDocumentTypeService instance, IToastService toastService)
|
||||||
|
: base(instance, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<DocumentCodeDetailsResponse[]> GetDocumentTypes()
|
||||||
|
=> TryAndToast(() => Instance.GetDocumentTypes());
|
||||||
|
public Task<IEnumerable<DocumentCodeDetailsAuditResponse>> GetAuditLogsForDocumentCodeDetail(string documentCode)
|
||||||
|
=> TryAndToast(() => Instance.GetAuditLogsForDocumentCodeDetail(documentCode));
|
||||||
|
public Task<DocumentCodeDetailsResponse> CreateDocumentCodeDetail(DocumentCodeDetailsRequest documentCodeDetail)
|
||||||
|
=> TryAndToast(() => Instance.CreateDocumentCodeDetail(documentCodeDetail), $"There was an issue saving {documentCodeDetail.DocumentCode}");
|
||||||
|
public Task<DocumentCodeDetailsResponse> UpdateDocumentCodeDetails(DocumentCodeDetailsRequest request)
|
||||||
|
=> TryAndToast(() => Instance.UpdateDocumentCodeDetails(request), $"There was an error saving document code details for Doc Code: {request.DocumentCode}");
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class DocumentTypeUploaderServiceErrorDecorator : ToastErrorServiceDecorator<IDocumentTypeUploaderService>, IDocumentTypeUploaderService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Document Type Uploader";
|
||||||
|
|
||||||
|
public DocumentTypeUploaderServiceErrorDecorator(IDocumentTypeUploaderService instance, IToastService toastService)
|
||||||
|
: base(instance, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DocumentMetadataUpdate(DocumentInformation document, EntityTypes selectedEntityType,
|
||||||
|
string entityId)
|
||||||
|
=> TryAndToast
|
||||||
|
(
|
||||||
|
() => Instance.DocumentMetadataUpdate(document, selectedEntityType, entityId),
|
||||||
|
"There was an error changing authorization for document. If problem persists, contact the IT team."
|
||||||
|
);
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Accounts;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Mixed;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Organizations;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Persons;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public class EntityServiceErrorDecorator : ToastErrorServiceDecorator<IEntityService>, IEntityService
|
||||||
|
{
|
||||||
|
protected override string ServiceName => "Core Api";
|
||||||
|
|
||||||
|
public EntityServiceErrorDecorator(IEntityService entityServiceImplementation, IToastService toastService)
|
||||||
|
: base(entityServiceImplementation, toastService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IEnumerable<Person>> GetPersonByEntityNumber(string entityNumber)
|
||||||
|
=> TryAndToast(() => Instance.GetPersonByEntityNumber(entityNumber));
|
||||||
|
public Task<IEnumerable<Organization>> GetOrganizationByEntityNumber(string entityNumber)
|
||||||
|
=> TryAndToast(() => Instance.GetOrganizationByEntityNumber(entityNumber));
|
||||||
|
public Task<IEnumerable<AccountOwnership>> GetOwnersByAccountNumber(long accountNumber, DateTime creationDate)
|
||||||
|
=> TryAndToast(() => Instance.GetOwnersByAccountNumber(accountNumber, creationDate));
|
||||||
|
public Task<int> GetCountPersonsByNameLike(PersonNameRequest request)
|
||||||
|
=> TryAndToast(() => Instance.GetCountPersonsByNameLike(request));
|
||||||
|
public Task<IEnumerable<Person>?> GetPersonsByNameLike(PersonNameRequest request, int limit)
|
||||||
|
=> TryAndToast(() => Instance.GetPersonsByNameLike(request, limit));
|
||||||
|
public Task<long> CountOrganizationNumbersByName(string organizationName)
|
||||||
|
=> TryAndToast(() => Instance.CountOrganizationNumbersByName(organizationName));
|
||||||
|
public Task<IEnumerable<Organization>?> GetOrganizationByOrganizationName(string organizationName, int limit)
|
||||||
|
=> TryAndToast(() => Instance.GetOrganizationByOrganizationName(organizationName, limit));
|
||||||
|
public Task<PersonOrganization> GetPersonOrOrganizationNumberByMemberId(long memberId)
|
||||||
|
=> TryAndToast(() => Instance.GetPersonOrOrganizationNumberByMemberId(memberId));
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
using Blazored.Toast.Services;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services.Decorators;
|
||||||
|
|
||||||
|
public abstract class ToastErrorServiceDecorator<TDecorated>: IDisposable
|
||||||
|
{
|
||||||
|
protected readonly TDecorated Instance;
|
||||||
|
protected abstract string ServiceName { get; }
|
||||||
|
|
||||||
|
private readonly IList<string> _currentMessageStack = new List<string>();
|
||||||
|
private int _errorCount = 0;
|
||||||
|
|
||||||
|
private readonly IToastService _toastService;
|
||||||
|
private CancellationTokenSource _pendingErrorToast = new();
|
||||||
|
private readonly SemaphoreSlim _self = new (1, 1);
|
||||||
|
|
||||||
|
protected ToastErrorServiceDecorator(TDecorated instance, IToastService toastService)
|
||||||
|
{
|
||||||
|
Instance = instance;
|
||||||
|
_toastService = toastService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<T> TryAndToast<T>(Func<Task<T>> action, string? failureMessage = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await action();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
QueueErrorToast(failureMessage);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task TryAndToast(Func<Task> action, string? failureMessage = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await action();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
QueueErrorToast(failureMessage);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueueErrorToast(string? message = null)
|
||||||
|
{
|
||||||
|
_pendingErrorToast.Cancel();
|
||||||
|
var thisCancellation = _pendingErrorToast = new CancellationTokenSource();
|
||||||
|
|
||||||
|
if(message is not null) _currentMessageStack.Add(message);
|
||||||
|
_errorCount++;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1), thisCancellation.Token);
|
||||||
|
if (thisCancellation.Token.IsCancellationRequested) return;
|
||||||
|
|
||||||
|
await _self.WaitAsync(TimeSpan.FromMilliseconds(500), thisCancellation.Token);
|
||||||
|
if (!thisCancellation.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
//todo ShowError takes a render fragment, would be great to show this as an html bulleted list
|
||||||
|
//initial attempts failed at rendering (breakpoint hit in the component but nothing rendered visibly)
|
||||||
|
//https://learn.microsoft.com/en-us/aspnet/core/blazor/advanced-scenarios?view=aspnetcore-7.0
|
||||||
|
|
||||||
|
var errorMessage = _currentMessageStack.Any()
|
||||||
|
? string.Join("\n", _currentMessageStack)
|
||||||
|
: $"{_errorCount} error(s) fetching data from {ServiceName}";
|
||||||
|
_toastService.ShowError(errorMessage);
|
||||||
|
|
||||||
|
_errorCount = default;
|
||||||
|
_currentMessageStack.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_self.Release();
|
||||||
|
}, thisCancellation.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_pendingErrorToast.Dispose();
|
||||||
|
_self.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination;
|
||||||
|
using LCU.Library.Internal.FileOperationsModels.Shared;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using LCU.Library.Internal.Security.Service;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class DocumentProcessingInfoService : IDocumentProcessingInfoService
|
||||||
|
{
|
||||||
|
private readonly IPostRequest _postRequest;
|
||||||
|
private readonly IPutRequest _putRequest;
|
||||||
|
private readonly DocumentIngestorApiConfiguration _documentIngestorApiConfiguration;
|
||||||
|
private readonly AuthenticationHelper _authenticationHelper;
|
||||||
|
private readonly IAzureAdAuthorizationService _azureAdAuthorizationService;
|
||||||
|
|
||||||
|
public DocumentProcessingInfoService(IPostRequest postRequest,
|
||||||
|
IPutRequest putRequest,
|
||||||
|
DocumentIngestorApiConfiguration documentIngestorApiConfiguration,
|
||||||
|
AuthenticationHelper authenticationHelper,
|
||||||
|
IAzureAdAuthorizationService azureAdAuthorizationService)
|
||||||
|
{
|
||||||
|
_postRequest = postRequest;
|
||||||
|
_putRequest = putRequest;
|
||||||
|
_documentIngestorApiConfiguration = documentIngestorApiConfiguration;
|
||||||
|
_authenticationHelper = authenticationHelper;
|
||||||
|
_azureAdAuthorizationService = azureAdAuthorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedResponse<DocumentProcessingInfo>> GetFilteredDocumentProcessingInfos(Filter filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _postRequest.AsyncWithNewton<Filter, PagedResponse<DocumentProcessingInfo>>(
|
||||||
|
_documentIngestorApiConfiguration.FilteredDocumentProcessingInfoEndpointUrl,
|
||||||
|
filter);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting FilteredDocumentProcessingInfos.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetDocumentsToEntityUnknownByFilter(Filter filter, int totalItems)
|
||||||
|
{
|
||||||
|
var filterCopy = filter.DeepClone();
|
||||||
|
filterCopy.Limit = totalItems;
|
||||||
|
|
||||||
|
var pagedResponse = await _postRequest.AsyncWithNewton<Filter, PagedResponse<DocumentProcessingInfo>>(
|
||||||
|
_documentIngestorApiConfiguration.FilteredDocumentProcessingInfoEndpointUrl,
|
||||||
|
filterCopy);
|
||||||
|
|
||||||
|
if (totalItems != pagedResponse?.Items.Count())
|
||||||
|
{
|
||||||
|
var errorMessage = $"local memory holds {totalItems}, actual total items is now {pagedResponse?.Items.Count()}";
|
||||||
|
Log.Error(errorMessage);
|
||||||
|
throw new ExpiredDataException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetDocumentToEntityUnknown(pagedResponse.Items.Select(x => x.DocumentProcessingInfoId).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetDocumentToEntityUnknown(params int[] documentProcessingInfoIds)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var entityUnknownRequest = new EntityUnknownRequest
|
||||||
|
{
|
||||||
|
UserName = _azureAdAuthorizationService.GetUserName(),
|
||||||
|
DocumentProcessingInfoIds = documentProcessingInfoIds.ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
await _postRequest.Async<EntityUnknownRequest>(new Uri(_documentIngestorApiConfiguration.SetDocumentsToEntityUnknownEndpointUrl), entityUnknownRequest);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error setting document entity to unknown.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DocumentProcessingInfo> UpdateStatus(DocumentProcessingInfoStatusRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _postRequest.AsyncWithNewton<DocumentProcessingInfoStatusRequest, DocumentProcessingInfo>(
|
||||||
|
_documentIngestorApiConfiguration.UpdateStatusEndpointUrl,
|
||||||
|
request);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting FilteredDocumentProcessingInfos.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetDocumentNotificationStatus(int memberNotificationInfoId, NotificationStatusTypes notificationStatus, string reason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var route = _documentIngestorApiConfiguration.SetDocumentNotificationStatusUrl
|
||||||
|
.Replace("{memberNotificationInfoId}", memberNotificationInfoId.ToString())
|
||||||
|
.Replace("{notificationStatus}", notificationStatus.ToString());
|
||||||
|
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentIngestor);
|
||||||
|
|
||||||
|
await _putRequest.Async<string>(new Uri(route), reason, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error setting document notification status.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task BulkSetDocumentNotificationStatus(Filter filter, NotificationStatusTypes status, string reason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var route = _documentIngestorApiConfiguration.BulkSetDocumentNotificationStatusUrl;
|
||||||
|
|
||||||
|
var request = new BulkNotificationStatusRequest
|
||||||
|
{
|
||||||
|
Filter = filter,
|
||||||
|
NotificationStatus = status,
|
||||||
|
ReasonMessage = reason
|
||||||
|
};
|
||||||
|
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentIngestor);
|
||||||
|
|
||||||
|
await _putRequest.Async<BulkNotificationStatusRequest>(new Uri(route), request, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error setting batch document notification status.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
using DocumentTypeRetriever;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using LCU.Library.Internal.Security.Service;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using Serilog;
|
||||||
|
using System.Text;
|
||||||
|
using DocumentAction = LCU.Library.Internal.DocumentModels.Requests.DocumentAction;
|
||||||
|
using DocumentActionNotificationRequest = LCU.Library.Internal.DocumentModels.Requests.DocumentActionNotificationRequest;
|
||||||
|
using DocumentAuditResponse = LCU.Library.Internal.DocumentModels.Responses.DocumentAuditResponse;
|
||||||
|
using DocumentMetadataResponse = LCU.Library.DocumentType.Models.Responses.External.DocumentMetadataResponse;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class DocumentTypeRetrieverService : IDocumentService, IDocumentTypeService
|
||||||
|
{
|
||||||
|
private readonly IGetRequest _getRequest;
|
||||||
|
private readonly IPostRequest _postRequest;
|
||||||
|
private readonly IPutRequest _putRequest;
|
||||||
|
private readonly DocumentTypeRetrieverApiConfiguration _configuration;
|
||||||
|
private readonly AuthenticationHelper _authenticationHelper;
|
||||||
|
private readonly IAzureAdAuthorizationService _azureAdAuthorizationService;
|
||||||
|
|
||||||
|
private const string UserIdHeaderKey = "userId";
|
||||||
|
|
||||||
|
public DocumentTypeRetrieverService
|
||||||
|
(
|
||||||
|
IGetRequest getRequest,
|
||||||
|
IPostRequest postRequest,
|
||||||
|
IPutRequest putRequest,
|
||||||
|
DocumentTypeRetrieverApiConfiguration configuration,
|
||||||
|
AuthenticationHelper authenticationHelper,
|
||||||
|
IAzureAdAuthorizationService azureAdAuthorizationService)
|
||||||
|
{
|
||||||
|
_getRequest = getRequest;
|
||||||
|
_postRequest = postRequest;
|
||||||
|
_putRequest = putRequest;
|
||||||
|
_configuration = configuration;
|
||||||
|
_authenticationHelper = authenticationHelper;
|
||||||
|
_azureAdAuthorizationService = azureAdAuthorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDocumentTypeService
|
||||||
|
|
||||||
|
public async Task<DocumentCodeDetailsResponse[]> GetDocumentTypes()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
|
||||||
|
return await _getRequest.AsyncWithNewton<DocumentCodeDetailsResponse[]>(new Uri(_configuration.DocCodeDetailsEndpointUrl + "/Get"), headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting DocumentTypeResponse.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DocumentCodeDetailsAuditResponse>> GetAuditLogsForDocumentCodeDetail(string documentCode)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
var uri = new Uri(_configuration.DocCodeDetailsAuditEndpointUrl.Replace("{documentCode}", documentCode));
|
||||||
|
|
||||||
|
var responses = await _getRequest.AsyncWithNewton<List<DocumentCodeDetailsAuditResponse>>(uri, headers);
|
||||||
|
|
||||||
|
return responses.OrderByDescending(x => x.TimeCreated);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting DocumentCodeDetailsAuditResponse.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DocumentCodeDetailsResponse> CreateDocumentCodeDetail(DocumentCodeDetailsRequest documentCodeDetail)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
|
||||||
|
return await _postRequest.AsyncWithNewton<DocumentCodeDetailsRequest, DocumentCodeDetailsResponse>(new Uri(_configuration.DocCodeDetailsEndpointUrl),
|
||||||
|
documentCodeDetail,
|
||||||
|
headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Creating DocumentCodeDetail.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DocumentCodeDetailsResponse> UpdateDocumentCodeDetails(DocumentCodeDetailsRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
|
||||||
|
|
||||||
|
return await _putRequest.AsyncWithNewton<DocumentCodeDetailsRequest, DocumentCodeDetailsResponse>(new Uri(_configuration.DocCodeDetailsEndpointUrl),
|
||||||
|
request,
|
||||||
|
headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error updating DocumentCodeDetailsRequest.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DocumentMetadataResponse>> GetDocuments(DocumentInformationFilteredSearch filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
var uri = new Uri(_configuration.Documents);
|
||||||
|
|
||||||
|
return await _postRequest.AsyncWithNewton<DocumentInformationFilteredSearch, IEnumerable<DocumentMetadataResponse>>(uri, filter, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting filtered DocumentMetadataResponse.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> CountDocuments(DocumentInformationFilter filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
var uri = new Uri(_configuration.CountDocuments);
|
||||||
|
|
||||||
|
return await _postRequest.AsyncWithNewton<DocumentInformationFilter, long>(uri, filter, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Counting Documents");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> StreamDocumentContent(Guid documentBlobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
var uri = new Uri(_configuration.DocumentContent.Replace("{documentBlobId}", documentBlobId.ToString()));
|
||||||
|
|
||||||
|
return await _postRequest.AsyncWithNewton<string, string>(uri, "", headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting document stream content");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExternalActionPerformed(Guid documentId, DocumentAction action, string source)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new DocumentActionNotificationRequest
|
||||||
|
{
|
||||||
|
Action = action,
|
||||||
|
Source = source
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeRetriever);
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
var uri = new Uri(_configuration.DocumentAction.Replace("{documentId}", documentId.ToString()));
|
||||||
|
|
||||||
|
await _postRequest.Async<DocumentActionNotificationRequest>(uri, request, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error getting document stream content");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DocumentAuditResponse>> GetDocumentLogs(Guid[] documentIds)
|
||||||
|
{
|
||||||
|
if (!documentIds.Any())
|
||||||
|
{
|
||||||
|
return Array.Empty<DocumentAuditResponse>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
|
||||||
|
const int maxQueryLength = 2048;
|
||||||
|
var queryParamByteLength = Encoding.ASCII.GetBytes($"&documentIds={Guid.Empty}").Length;
|
||||||
|
var requestCount = (int)Math.Ceiling((double)(documentIds.Length * queryParamByteLength) / maxQueryLength);
|
||||||
|
var idsPerRequest = (int)Math.Ceiling((double)(documentIds.Length / requestCount));
|
||||||
|
var splitQueries = new List<string>();
|
||||||
|
for (var i = 0; i < requestCount; i++)
|
||||||
|
{
|
||||||
|
splitQueries.Add($"?{string.Join('&', documentIds.Skip(i*idsPerRequest).Take((i+1)*idsPerRequest).Select(id => $"documentIds={id}"))}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await Task.WhenAll(splitQueries.Select(async query =>
|
||||||
|
{
|
||||||
|
return await _getRequest.AsyncWithNewton<IEnumerable<DocumentAuditResponse>>($"{_configuration.DocumentAudit}{query}", headers);
|
||||||
|
}))
|
||||||
|
).SelectMany(l => l).OrderByDescending(l => l.Date);
|
||||||
|
}
|
||||||
|
}
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
using LCU.Library.DocumentType.Enums;
|
||||||
|
using System.Text;
|
||||||
|
using LCU.Library.DocumentType.Extensions;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
using LCU.Library.Internal.Security.Service;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class DocumentTypeUploaderService : IDocumentTypeUploaderService
|
||||||
|
{
|
||||||
|
private readonly IPutRequest _putRequest;
|
||||||
|
private readonly DocumentTypeUploaderApiConfiguration _configuration;
|
||||||
|
private readonly AuthenticationHelper _authenticationHelper;
|
||||||
|
private readonly IAzureAdAuthorizationService _azureAdAuthorizationService;
|
||||||
|
|
||||||
|
private const string UserIdHeaderKey = "userId";
|
||||||
|
|
||||||
|
public DocumentTypeUploaderService
|
||||||
|
(
|
||||||
|
IPutRequest putRequest,
|
||||||
|
DocumentTypeUploaderApiConfiguration configuration,
|
||||||
|
AuthenticationHelper authenticationHelper,
|
||||||
|
IAzureAdAuthorizationService azureAdAuthorizationService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_putRequest = putRequest;
|
||||||
|
_configuration = configuration;
|
||||||
|
_authenticationHelper = authenticationHelper;
|
||||||
|
_azureAdAuthorizationService = azureAdAuthorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AccountDocumentMetadataUpdate(Guid documentId, object request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeUploader);
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
var uri = new Uri(_configuration.AccountDocumentMetadataUpdate.Replace("{documentId}", documentId.ToString()));
|
||||||
|
|
||||||
|
await _putRequest.Async<object>(uri, request, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error during AccountDocumentMetadataUpdate");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PrimaryOwnerDocumentMetadataUpdate(Guid documentId, object request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = await _authenticationHelper.CreateApiSecurityHeadersWithAuth(DownstreamWebService.DocumentTypeUploader);
|
||||||
|
headers.Add(UserIdHeaderKey, _azureAdAuthorizationService.GetUserName());
|
||||||
|
var uri = new Uri(_configuration.PrimaryOwnerDocumentMetadataUpdate.Replace("{documentId}", documentId.ToString()));
|
||||||
|
|
||||||
|
await _putRequest.Async<object>(uri, request, headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error during AccountDocumentMetadataUpdate");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DocumentMetadataUpdate(DocumentInformation document, EntityTypes selectedEntityType, string entityId)
|
||||||
|
{
|
||||||
|
if (EnumExtensions.IsDocumentTypeOf<PrimaryOwnerDocumentTypes>(document.DocumentCode))
|
||||||
|
{
|
||||||
|
string personNumber = null;
|
||||||
|
string orgNumber = null;
|
||||||
|
|
||||||
|
if(selectedEntityType == EntityTypes.Person)
|
||||||
|
{
|
||||||
|
personNumber = entityId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
orgNumber = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new
|
||||||
|
{
|
||||||
|
PrimaryOwnerAttribute = new
|
||||||
|
{
|
||||||
|
AccountNumber = document.AccountNumber.ToString(),
|
||||||
|
OrganizationNumber = orgNumber,
|
||||||
|
PersonNumber = personNumber
|
||||||
|
},
|
||||||
|
PrimaryOwnerDocumentMetadata = new
|
||||||
|
{
|
||||||
|
CreatedDate = document.CreatedDate,
|
||||||
|
DataSource = document.DataSource,
|
||||||
|
Source = document.Source,
|
||||||
|
Name = document.Name,
|
||||||
|
TraceId = document.TraceId,
|
||||||
|
Type = document.DocumentCode
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await PrimaryOwnerDocumentMetadataUpdate(document.DocumentId, request);
|
||||||
|
}
|
||||||
|
else if (EnumExtensions.IsDocumentTypeOf<AccountDocumentTypes>(document.DocumentCode))
|
||||||
|
{
|
||||||
|
var request = new
|
||||||
|
{
|
||||||
|
AccountAttribute = new
|
||||||
|
{
|
||||||
|
AccountNumber = document.AccountNumber.ToString(),
|
||||||
|
OrganizationNumbers = document.OrganizationNumbers.ToArray(),
|
||||||
|
PersonNumbers = document.PersonNumbers.ToArray()
|
||||||
|
},
|
||||||
|
AccountDocumentMetadata = new
|
||||||
|
{
|
||||||
|
CreatedDate = document.CreatedDate,
|
||||||
|
DataSource = document.DataSource,
|
||||||
|
Source = document.Source,
|
||||||
|
Name = document.Name,
|
||||||
|
TraceId = document.TraceId,
|
||||||
|
Type = document.DocumentCode
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await AccountDocumentMetadataUpdate(document.DocumentId, request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"document code {document.DocumentCode} does not support updates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class ExpiredDataException : Exception
|
||||||
|
{
|
||||||
|
public ExpiredDataException(string message)
|
||||||
|
:base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IDocumentProcessingInfoService
|
||||||
|
{
|
||||||
|
Task SetDocumentsToEntityUnknownByFilter(Filter filter, int totalItems);
|
||||||
|
Task SetDocumentToEntityUnknown(params int[] documentProcessingInfoIds);
|
||||||
|
Task<PagedResponse<DocumentProcessingInfo>> GetFilteredDocumentProcessingInfos(Filter filter);
|
||||||
|
Task<DocumentProcessingInfo> UpdateStatus(DocumentProcessingInfoStatusRequest request);
|
||||||
|
Task SetDocumentNotificationStatus(int documentProcessingInfoId, NotificationStatusTypes notificationStatus, string reason);
|
||||||
|
Task BulkSetDocumentNotificationStatus(Filter filter, NotificationStatusTypes status, string reason);
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IDocumentReprocessingService
|
||||||
|
{
|
||||||
|
Task ReprocessAllNotificationDataFailures();
|
||||||
|
Task QueueDocumentReprocessingWithOverride(OwnerOverrideInfo ownerOverrideInfo);
|
||||||
|
Task QueueDocumentReprocessing(int documentProcessingInfoId);
|
||||||
|
Task QueueDocumentReprocessingAllCriticalFailures();
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
using DocumentTypeRetriever;
|
||||||
|
using DocumentAction = LCU.Library.Internal.DocumentModels.Requests.DocumentAction;
|
||||||
|
using DocumentAuditResponse = LCU.Library.Internal.DocumentModels.Responses.DocumentAuditResponse;
|
||||||
|
using DocumentMetadataResponse = LCU.Library.DocumentType.Models.Responses.External.DocumentMetadataResponse;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IDocumentService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<DocumentMetadataResponse>> GetDocuments(DocumentInformationFilteredSearch filter);
|
||||||
|
Task<long> CountDocuments(DocumentInformationFilter filter);
|
||||||
|
Task<string> StreamDocumentContent(Guid documentBlobId);
|
||||||
|
Task ExternalActionPerformed(Guid documentId, DocumentAction action, string source);
|
||||||
|
Task<IEnumerable<DocumentAuditResponse>> GetDocumentLogs(Guid[] documentIds);
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
using DocumentTypeRetriever;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IDocumentTypeService
|
||||||
|
{
|
||||||
|
Task<DocumentCodeDetailsResponse[]> GetDocumentTypes();
|
||||||
|
Task<IEnumerable<DocumentCodeDetailsAuditResponse>> GetAuditLogsForDocumentCodeDetail(string documentCode);
|
||||||
|
Task<DocumentCodeDetailsResponse> CreateDocumentCodeDetail(DocumentCodeDetailsRequest documentCodeDetail);
|
||||||
|
Task<DocumentCodeDetailsResponse> UpdateDocumentCodeDetails(DocumentCodeDetailsRequest request);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IDocumentTypeUploaderService
|
||||||
|
{
|
||||||
|
Task DocumentMetadataUpdate(DocumentInformation document, EntityTypes selectedEntityType, string entityId);
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Accounts;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Mixed;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Organizations;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Persons;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public interface IEntityService
|
||||||
|
{
|
||||||
|
public Task<IEnumerable<Person>> GetPersonByEntityNumber(string entityNumber);
|
||||||
|
public Task<IEnumerable<Organization>> GetOrganizationByEntityNumber(string entityNumber);
|
||||||
|
public Task<IEnumerable<AccountOwnership>> GetOwnersByAccountNumber(long accountNumber, DateTime creationDate);
|
||||||
|
Task<int> GetCountPersonsByNameLike(PersonNameRequest request);
|
||||||
|
Task<IEnumerable<Person>?> GetPersonsByNameLike(PersonNameRequest request, int limit);
|
||||||
|
Task<long> CountOrganizationNumbersByName(string organizationName);
|
||||||
|
Task<IEnumerable<Organization>?> GetOrganizationByOrganizationName(string organizationName, int limit);
|
||||||
|
Task<PersonOrganization> GetPersonOrOrganizationNumberByMemberId(long memberId);
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Entities;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using LCU.Library.Internal.Security.Service;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Authentication;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using Serilog;
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
|
||||||
|
public class QueueDocumentReprocessingService : IDocumentReprocessingService
|
||||||
|
{
|
||||||
|
private readonly IPostRequest _postRequest;
|
||||||
|
private readonly DocumentIngestorApiConfiguration _documentIngestorApiConfiguration;
|
||||||
|
private readonly AuthenticationHelper _authenticationHelper;
|
||||||
|
private readonly IAzureAdAuthorizationService _azureAdAuthorizationService;
|
||||||
|
|
||||||
|
public QueueDocumentReprocessingService(IPostRequest postRequest,
|
||||||
|
DocumentIngestorApiConfiguration documentIngestorApiConfiguration,
|
||||||
|
AuthenticationHelper authenticationHelper,
|
||||||
|
IAzureAdAuthorizationService azureAdAuthorizationService)
|
||||||
|
{
|
||||||
|
_postRequest = postRequest;
|
||||||
|
_documentIngestorApiConfiguration = documentIngestorApiConfiguration;
|
||||||
|
_authenticationHelper = authenticationHelper;
|
||||||
|
_azureAdAuthorizationService = azureAdAuthorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueDocumentReprocessing(int documentProcessingInfoId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = _documentIngestorApiConfiguration.
|
||||||
|
QueueReprocessDocumentByIdEndpointUrl.Replace("{documentProcessingInfoId}", documentProcessingInfoId.ToString())
|
||||||
|
.Replace("{userName}", _azureAdAuthorizationService.GetUserName());
|
||||||
|
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
|
||||||
|
await _postRequest.Async<string>(new Uri(url), "", headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Queueing Document Processing Request." +
|
||||||
|
$"DocumentProcessingInfoId:{documentProcessingInfoId}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueDocumentReprocessingWithOverride(OwnerOverrideInfo ownerOverrideInfo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ownerOverrideInfo.UserName = _azureAdAuthorizationService.GetUserName();
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
|
||||||
|
await _postRequest.Async<OwnerOverrideInfo>(new Uri(_documentIngestorApiConfiguration.QueueReprocessDocumentWithOverrideByIdEndpointUrl),
|
||||||
|
ownerOverrideInfo,
|
||||||
|
headers);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Queue Document Reprocessing With Override. " +
|
||||||
|
$"DocumentProcessingInfoId:{ownerOverrideInfo.DocumentProcessingInfoId}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueDocumentReprocessingAllCriticalFailures()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri(_documentIngestorApiConfiguration.
|
||||||
|
QueueReprocessDocumentAllCriticalFailuresEndpointUrl.Replace("{userName}", _azureAdAuthorizationService.GetUserName()));
|
||||||
|
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
|
||||||
|
await _postRequest.Async<string>(uri, "", headers, 0, _documentIngestorApiConfiguration.QueueReprocessDocumentAllCriticalFailuresTimeoutSeconds);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Queueing Document Processing Request For All Critical Failures.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ReprocessAllNotificationDataFailures()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri(_documentIngestorApiConfiguration.
|
||||||
|
QueueReprocessCompletionAllNotificationDataFailuresUrl
|
||||||
|
.Replace("{userName}", _azureAdAuthorizationService.GetUserName()));
|
||||||
|
|
||||||
|
var headers = _authenticationHelper.CreateApiSecurityHeaders(new Dictionary<string, string>());
|
||||||
|
|
||||||
|
await _postRequest.Async<string>(uri, "", headers, 0, _documentIngestorApiConfiguration.QueueReprocessCompletionAllNotificationDataFailuresTimeoutSeconds);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Error Queueing Document Processing Request For All Data Failures.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.Admin">
|
||||||
|
@ChildContent
|
||||||
|
</AuthorizeView>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
<AdminComponent>
|
||||||
|
<div class="dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" role="button" id="dropdownMenuButton" data-toggle="dropdown" data-boundary="viewport" data-flip="true" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="oi oi-cog mr-2" aria-hidden="true"></span> Admin
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.DocCodeDetails">
|
||||||
|
<a class="dropdown-item text-dark" href="DocCodeDetails">Doc Code Details Table</a>
|
||||||
|
</AuthorizeView>
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.ReprocessingTable">
|
||||||
|
<a class="dropdown-item text-dark" href="ReprocessingTable">Reprocessing Table</a>
|
||||||
|
</AuthorizeView>
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.ITReprocessingTable">
|
||||||
|
<a class="dropdown-item text-dark" href="ITReprocessingTable">IT Reprocessing Table</a>
|
||||||
|
</AuthorizeView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AdminComponent>
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
@inject IJSTooltips JSTooltips;
|
||||||
|
|
||||||
|
<div class="form-row col-12 mt-3">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label class="col-12 col-form-label">Custom Filter Key <i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="Advanced: you must know the exact index key and value for this filtering method to work."></i>:</label>
|
||||||
|
<InputText id="CustomFilter"
|
||||||
|
type="text"
|
||||||
|
class="form-control col-12"
|
||||||
|
placeholder="Type to add custom filter by key..."
|
||||||
|
@bind-Value="@_customFilterKey"
|
||||||
|
@oninput="@(change => { _customFilterKey = change.Value!.ToString(); StateHasChanged(); })"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-2">
|
||||||
|
<div class="col flex-end">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
title="Add Filter"
|
||||||
|
@onclick="@AddCustomFilter"
|
||||||
|
disabled="@(string.IsNullOrWhiteSpace(_customFilterKey) || Filter.CustomIndexFileFiltersEquals.ContainsKey(_customFilterKey))">
|
||||||
|
Add Filter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
@foreach (var filter in Filter.CustomIndexFileFiltersEquals)
|
||||||
|
{
|
||||||
|
var key = filter.Key;
|
||||||
|
var value = filter.Value;
|
||||||
|
<div class="">
|
||||||
|
<label class="col-12 col-form-label">Custom Filter - @(key):</label>
|
||||||
|
<InputText type="text"
|
||||||
|
id="@key"
|
||||||
|
style="display: inline-block;"
|
||||||
|
class="col-11 form-control"
|
||||||
|
placeholder="Type to filter custom value..."
|
||||||
|
@bind-Value="@value"
|
||||||
|
@oninput="@(change => FilterHasChanged(key, change.Value!.ToString()))"/>
|
||||||
|
<i class="col-auto fa fa-times pr-0" style="cursor: pointer;" onclick="@(() => RemoveCustomFilter(key))"></i>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="col-12 col-form-label" style="display:inline-block;">
|
||||||
|
<ValidationMessage For="@(() => Filter.CustomIndexFileFiltersEquals)"/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback Validate { get; set; }
|
||||||
|
|
||||||
|
private string _customFilterKey = "";
|
||||||
|
|
||||||
|
private async void ValidateForm() => await Validate.InvokeAsync();
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersEquals ??= new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) JSTooltips.Enable();
|
||||||
|
|
||||||
|
return base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCustomFilter()
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersEquals[_customFilterKey] = string.Empty;
|
||||||
|
_customFilterKey = string.Empty;
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveCustomFilter(string key)
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersEquals.Remove(key);
|
||||||
|
ValidateForm();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FilterHasChanged(string key, string value)
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersEquals[key] = value;
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Enums;
|
||||||
|
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label text-nowrap">Doc Creation Date Start:</label>
|
||||||
|
<InputDate id="CreatedDateStart" class="form-control" TValue="DateTime?" @bind-Value="@Filter.DocumentProcessingFilter.Filter.CreatedDateMinimum"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label text-nowrap">Doc Creation Date End:</label>
|
||||||
|
<InputDate id="CreatedDateEnd" class="form-control" TValue="DateTime?" Value="@Filter.DocumentProcessingFilter.Filter.CreatedDateMaximum" ValueExpression="@(() => Filter.DocumentProcessingFilter.Filter.CreatedDateMaximum)" ValueChanged="@SetMaxInclusive"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="col-12 col-form-label">
|
||||||
|
<span class="text-nowrap">Ownership Criteria:</span>
|
||||||
|
</label>
|
||||||
|
<BlazoredTypeahead SearchMethod="@FilterMemberExclusions"
|
||||||
|
TValue="@string"
|
||||||
|
TItem="@string"
|
||||||
|
Value="@SelectedOption"
|
||||||
|
ValueExpression="@(() => SelectedOption)"
|
||||||
|
ValueChanged="@MemberExclusionTypeSelected"
|
||||||
|
EnableDropDown="true"
|
||||||
|
placeholder="Select an option...">
|
||||||
|
<SelectedTemplate>
|
||||||
|
@context
|
||||||
|
</SelectedTemplate>
|
||||||
|
<ResultTemplate>
|
||||||
|
@context
|
||||||
|
</ResultTemplate>
|
||||||
|
</BlazoredTypeahead>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="col-12 col-form-label"> </label>
|
||||||
|
@if (Filter?.OwnershipType == OwnershipTypes.PersonName)
|
||||||
|
{
|
||||||
|
<InputText type="search"
|
||||||
|
id="DocTypeFilter"
|
||||||
|
@bind-Value="@Filter.OwnershipValue"
|
||||||
|
@oninput="@(i => OnOwnershipChange.InvokeAsync((Filter.OwnershipType, i.Value.ToString())))"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="First Name"
|
||||||
|
spellcheck="false"
|
||||||
|
autocomplete="false"/>
|
||||||
|
<InputText type="search"
|
||||||
|
id="DocTypeFilterAdditional"
|
||||||
|
@bind-Value="@Filter.AdditionalOwnershipValue"
|
||||||
|
@oninput="@(i => OnAdditionalOwnershipChange.InvokeAsync((Filter.OwnershipType, i.Value.ToString())))"
|
||||||
|
class="form-control mt-2"
|
||||||
|
placeholder="Last Name"
|
||||||
|
spellcheck="false"
|
||||||
|
autocomplete="false"/>
|
||||||
|
}
|
||||||
|
else if (Filter?.OwnershipType == OwnershipTypes.OrganizationName)
|
||||||
|
{
|
||||||
|
<InputText type="search"
|
||||||
|
id="DocTypeFilter"
|
||||||
|
@bind-Value="@Filter.OwnershipValue"
|
||||||
|
@oninput="@(i => OnOwnershipChange.InvokeAsync((Filter.OwnershipType, i.Value.ToString())))"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Organization Name"
|
||||||
|
spellcheck="false"
|
||||||
|
autocomplete="false"/>
|
||||||
|
}
|
||||||
|
else if (Filter?.OwnershipType == OwnershipTypes.MemberNumber ||
|
||||||
|
Filter?.OwnershipType == OwnershipTypes.AccountNumber ||
|
||||||
|
Filter?.OwnershipType == OwnershipTypes.OrganizationNumber ||
|
||||||
|
Filter?.OwnershipType == OwnershipTypes.PersonNumber
|
||||||
|
)
|
||||||
|
{
|
||||||
|
<InputText type="number"
|
||||||
|
id="DocTypeFilter"
|
||||||
|
@bind-Value="@Filter.OwnershipValue"
|
||||||
|
@oninput="@(i => OnOwnershipChange.InvokeAsync((Filter.OwnershipType, i.Value.ToString())))"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="########"
|
||||||
|
spellcheck="false"
|
||||||
|
autocomplete="false"/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public BusinessViewFilter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<(OwnershipTypes? type, string? value)> OnOwnershipChange { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<(OwnershipTypes? type, string? value)> OnAdditionalOwnershipChange { get; set; }
|
||||||
|
|
||||||
|
private string SelectedOption => Filter is { OwnershipType: {} type } ? _ownershipFilters[type] : string.Empty;
|
||||||
|
|
||||||
|
private static Dictionary<OwnershipTypes, string> _ownershipFilters = new()
|
||||||
|
{
|
||||||
|
{ OwnershipTypes.MemberNumber, "Member #" }, // CoreApi | api/core/members/{memberNumber}
|
||||||
|
{ OwnershipTypes.AccountNumber, "Account #" }, // DocumentInformation
|
||||||
|
{ OwnershipTypes.PersonName, "Person Name" }, // CoreApi | api/core/Person/GetPerson
|
||||||
|
{ OwnershipTypes.OrganizationName, "Organization Name" }, // CoreApi | api/core/Organization/{organizationName}
|
||||||
|
{ OwnershipTypes.OrganizationNumber, "Org #" }, // DocumentInformation
|
||||||
|
{ OwnershipTypes.PersonNumber, "Person #" } // DocumentInformation
|
||||||
|
};
|
||||||
|
|
||||||
|
private Task<IEnumerable<string>> FilterMemberExclusions(string search)
|
||||||
|
{
|
||||||
|
return Task.FromResult(_ownershipFilters.Where(s => s.Value.ToString().Contains(search, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MemberExclusionTypeSelected(string ownershipType)
|
||||||
|
{
|
||||||
|
Filter.OwnershipType = ownershipType is {} type
|
||||||
|
? _ownershipFilters.FirstOrDefault(x => x.Value.Equals(type, StringComparison.OrdinalIgnoreCase)).Key
|
||||||
|
: null;
|
||||||
|
|
||||||
|
Filter.OwnershipValue = string.Empty;
|
||||||
|
Filter.AdditionalOwnershipValue = string.Empty;
|
||||||
|
|
||||||
|
OnOwnershipChange.InvokeAsync((Filter.OwnershipType, Filter.OwnershipValue));
|
||||||
|
if(Filter.OwnershipType == OwnershipTypes.PersonName) OnAdditionalOwnershipChange.InvokeAsync((Filter.OwnershipType, Filter.AdditionalOwnershipValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetMaxInclusive(DateTime? createdDateMax)
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.Filter.CreatedDateMaximum = createdDateMax?.AddDays(1).AddTicks(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label">Doc Business Name:</label>
|
||||||
|
<input class="form-control" type="search" @bind="@Filter.DocumentBusinessName"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label">Doc Member Name:</label>
|
||||||
|
<input class="form-control" type="search" @bind="@Filter.DocumentMemberFacingName"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="col-12 col-form-label">Exclude from Member View:</label>
|
||||||
|
<BlazoredTypeahead SearchMethod="@FilterMemberExclusions"
|
||||||
|
TValue="@string"
|
||||||
|
TItem="@string"
|
||||||
|
Value="@_memberExclusionType"
|
||||||
|
ValueExpression="@(() => _memberExclusionType)"
|
||||||
|
ValueChanged="@MemberExclusionTypeSelected"
|
||||||
|
EnableDropDown="true"
|
||||||
|
placeholder="Select an option...">
|
||||||
|
<SelectedTemplate>
|
||||||
|
@context
|
||||||
|
</SelectedTemplate>
|
||||||
|
<ResultTemplate>
|
||||||
|
@context
|
||||||
|
</ResultTemplate>
|
||||||
|
</BlazoredTypeahead>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public DocumentCodeDetailsFilter Filter { get; set; }
|
||||||
|
|
||||||
|
private string _memberExclusionType = string.Empty;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_memberExclusionType = Filter.ExcludeFromMemberView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<IEnumerable<string>> FilterMemberExclusions(string search)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new[] { "None", "Date Range", "All" }.Where(s => s.ToString().Contains(search, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MemberExclusionTypeSelected(string memberExclusion)
|
||||||
|
{
|
||||||
|
Filter.ExcludeFromMemberView = _memberExclusionType = memberExclusion;
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label">Doc Ingestion Date Start:</label>
|
||||||
|
<InputDate id="CreatedDateStart"
|
||||||
|
class="form-control"
|
||||||
|
TValue="DateTime?"
|
||||||
|
@bind-Value="@Filter.CreatedDateStart"
|
||||||
|
@onblur="@ValidateForm"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" style="padding-left: 4px;">
|
||||||
|
<label class="col-12 col-form-label">Doc Ingestion Date End:</label>
|
||||||
|
<InputDate id="CreatedDateEnd"
|
||||||
|
class="form-control"
|
||||||
|
TValue="DateTime?"
|
||||||
|
Value="@Filter.CreatedDateEnd"
|
||||||
|
ValueExpression="@(() => Filter.CreatedDateEnd)"
|
||||||
|
ValueChanged="@SetMaxInclusive"
|
||||||
|
@onblur="@ValidateForm"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="col-12 col-form-label">Doc Business Name:</label>
|
||||||
|
<InputText type="search" id="DocBusinessNameFilter" @bind-Value="Filter.DocBusinessName" class="form-control" placeholder="Type to filter Doc Business Name records..."
|
||||||
|
spellcheck="false" autocomplete="false"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="col-12 col-form-label">Doc Type:</label>
|
||||||
|
<InputText type="search" id="DocTypeFilter" @bind-Value="Filter.DocType" class="form-control" placeholder="Type to filter Doc Type records..."
|
||||||
|
spellcheck="false" autocomplete="false"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@if (AdditionalFilters != null && Filter != null)
|
||||||
|
{
|
||||||
|
@AdditionalFilters(Filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<Filter> AdditionalFilters { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback Validate { get; set; }
|
||||||
|
|
||||||
|
private async void ValidateForm() => await Validate.InvokeAsync();
|
||||||
|
|
||||||
|
private void SetMaxInclusive(DateTime? createdDateEnd)
|
||||||
|
{
|
||||||
|
Filter.CreatedDateEnd = createdDateEnd?.AddDays(1).AddTicks(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
|
||||||
|
<label class="col-12 col-form-label">Notification Status:</label>
|
||||||
|
<BlazoredTypeahead SearchMethod="@FilterDocumentProcessingInfosByStatus"
|
||||||
|
TValue="@string"
|
||||||
|
TItem="@string"
|
||||||
|
Values="@_selectedStatuses"
|
||||||
|
ValuesExpression="@(() => _selectedStatuses)"
|
||||||
|
ValuesChanged="@NotificationStatusTypeSelected"
|
||||||
|
EnableDropDown="true"
|
||||||
|
placeholder="Select an option...">
|
||||||
|
<SelectedTemplate>
|
||||||
|
@context
|
||||||
|
</SelectedTemplate>
|
||||||
|
<ResultTemplate>
|
||||||
|
@context
|
||||||
|
</ResultTemplate>
|
||||||
|
</BlazoredTypeahead>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
private static readonly IReadOnlySet<string> StatusList = Enum.GetValues<NotificationStatusTypes>()
|
||||||
|
.Select(x => x.ToString())
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
private IList<string> _selectedStatuses = new List<string>();
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
=> _selectedStatuses = (Filter.NotificationStatusFilters ?? Enumerable.Empty<NotificationStatusTypes>())
|
||||||
|
.Select(f => f.ToString())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
private async Task<IEnumerable<string>> FilterDocumentProcessingInfosByStatus(string search)
|
||||||
|
=> StatusList.Where(s => s.Contains(search, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
private void NotificationStatusTypeSelected(IList<string> selectedStatuses)
|
||||||
|
=> Filter.NotificationStatusFilters = (_selectedStatuses = selectedStatuses)
|
||||||
|
.Select(Enum.Parse<NotificationStatusTypes>)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
|
||||||
|
<label class="col-12 px-3 col-form-label">Process Status:</label>
|
||||||
|
<BlazoredTypeahead SearchMethod="@FilterDocumentProcessingInfosByStatus"
|
||||||
|
TValue="@string"
|
||||||
|
TItem="@string"
|
||||||
|
Values="@_selectedStatuses"
|
||||||
|
ValuesExpression="@(() => _selectedStatuses)"
|
||||||
|
ValuesChanged="@ProcessStatusTypeSelected"
|
||||||
|
EnableDropDown="true"
|
||||||
|
placeholder="Select an option...">
|
||||||
|
<SelectedTemplate>
|
||||||
|
@context
|
||||||
|
</SelectedTemplate>
|
||||||
|
<ResultTemplate>
|
||||||
|
@context
|
||||||
|
</ResultTemplate>
|
||||||
|
</BlazoredTypeahead>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback Validate { get; set; }
|
||||||
|
|
||||||
|
private const string Unknown = "Unknown";
|
||||||
|
private const string Delimiter = "-";
|
||||||
|
|
||||||
|
private IList<string> _selectedStatuses = new List<string>();
|
||||||
|
|
||||||
|
private async void ValidateForm() => await Validate.InvokeAsync();
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_selectedStatuses = Filter.StatusFilters
|
||||||
|
.Where(f => f.IsEnabled)
|
||||||
|
.Select(f => f.IsUnknown ? $"{Unknown}{Delimiter}{f.StatusType}" : f.StatusType.ToString())
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<IEnumerable<string>> FilterDocumentProcessingInfosByStatus(string search)
|
||||||
|
{
|
||||||
|
var statusList = Enum.GetValues<ProcessStatusTypes>().Select(x => x.ToString()).ToList();
|
||||||
|
statusList.AddRange(AddUnknownEnabledStatuses(statusList));
|
||||||
|
|
||||||
|
return Task.FromResult(statusList.Where(s => s.ToString().Contains(search, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string?> AddUnknownEnabledStatuses(IEnumerable<string> statusList)
|
||||||
|
{
|
||||||
|
return AppConfiguration.UnknownEnabledStatuses
|
||||||
|
.Split(',')
|
||||||
|
.Select(status => statusList.Contains(status)
|
||||||
|
? $"{Unknown}{Delimiter}{status}"
|
||||||
|
: null).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessStatusTypeSelected(IList<string> selectedStatuses)
|
||||||
|
{
|
||||||
|
Filter.StatusFilters = (_selectedStatuses = selectedStatuses)
|
||||||
|
.Select(statusString =>
|
||||||
|
{
|
||||||
|
if (statusString.StartsWith(Unknown))
|
||||||
|
{
|
||||||
|
return new StatusFilter
|
||||||
|
{
|
||||||
|
IsEnabled = true,
|
||||||
|
IsUnknown = true,
|
||||||
|
StatusType = Enum.Parse<ProcessStatusTypes>(statusString.Split(Delimiter).Last())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new StatusFilter
|
||||||
|
{
|
||||||
|
IsEnabled = true,
|
||||||
|
IsUnknown = false,
|
||||||
|
StatusType = Enum.Parse<ProcessStatusTypes>(statusString)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
).ToList();
|
||||||
|
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<label class="col-12 px-3 col-form-label">
|
||||||
|
Search Criteria:
|
||||||
|
<i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top"
|
||||||
|
title="Note: The search will return only data for the specified document sources."></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<BlazoredTypeahead SearchMethod="FilterDocumentProcessingInfosBySearchCriteria"
|
||||||
|
Value="@_selectedCriteria"
|
||||||
|
ValueChanged="@CriteriaChanged"
|
||||||
|
ValueExpression="@( () => _selectedCriteria )"
|
||||||
|
EnableDropDown="true"
|
||||||
|
TValue="@string"
|
||||||
|
TItem="@string"
|
||||||
|
placeholder="Select an option...">
|
||||||
|
<SelectedTemplate>
|
||||||
|
@context
|
||||||
|
</SelectedTemplate>
|
||||||
|
<ResultTemplate>
|
||||||
|
@context
|
||||||
|
</ResultTemplate>
|
||||||
|
</BlazoredTypeahead>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_selectedCriteria))
|
||||||
|
{
|
||||||
|
<InputText id="CustomFilter"
|
||||||
|
type="text"
|
||||||
|
class="form-control col-12"
|
||||||
|
placeholder="Search Value"
|
||||||
|
@bind-Value="@_criteriaValue"
|
||||||
|
@oninput="@(e => ProcessCriteriaValue(e.Value!.ToString()))"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersLikeOr = null;
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-row col-12">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_selectedCriteria))
|
||||||
|
{
|
||||||
|
<label class="col-12 px-3 col-form-label" style="color: #777; font-size: 85%;">
|
||||||
|
Returned Types: @string.Join(", ", _criteria[_selectedCriteria]._docTypes.Select(x => Enum.GetName(typeof(IngestorTypes), x)))
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<span class="col-12 col-form-label" style="display:inline-block;">
|
||||||
|
<ValidationMessage For="@(() => Filter.CustomIndexFileFiltersLikeOr)"/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback Validate { get; set; }
|
||||||
|
|
||||||
|
private string _selectedCriteria = string.Empty;
|
||||||
|
private string _criteriaValue = string.Empty;
|
||||||
|
|
||||||
|
private Dictionary<string, (IEnumerable<IngestorTypes> _docTypes, IEnumerable<string> _filters)> _criteria = new()
|
||||||
|
{
|
||||||
|
{"Account Number",
|
||||||
|
(
|
||||||
|
new List<IngestorTypes> { IngestorTypes.XdiDnaNotice,
|
||||||
|
IngestorTypes.OsgDnaNoticesDocument,
|
||||||
|
IngestorTypes.FosTaxStatement,
|
||||||
|
IngestorTypes.FosLoanStatement,
|
||||||
|
IngestorTypes.FosDnaDocument },
|
||||||
|
new List<string> {"AccountNumber"}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{"Member Number Or User Id",
|
||||||
|
(
|
||||||
|
new List<IngestorTypes> { IngestorTypes.OsgDnaNoticesDocument },
|
||||||
|
new List<string> {"MemberNumberOrUserId"}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{"Entity Name",
|
||||||
|
(
|
||||||
|
new List<IngestorTypes> { IngestorTypes.XdiDnaNotice,
|
||||||
|
IngestorTypes.XdiActionNotice,
|
||||||
|
IngestorTypes.FosTaxStatement,
|
||||||
|
IngestorTypes.FosLoanStatement,
|
||||||
|
IngestorTypes.FosDnaDocument },
|
||||||
|
new List<string> {"EntityName", "FullName"}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private async void ValidateForm() => await Validate.InvokeAsync();
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
if (Filter.CustomIndexFileFiltersLikeOr is null or { Count: 0 })
|
||||||
|
{
|
||||||
|
_selectedCriteria = _criteriaValue = string.Empty;
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrWhiteSpace(_selectedCriteria))
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersLikeOr = null;
|
||||||
|
_criteriaValue = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CriteriaChanged(string result)
|
||||||
|
{
|
||||||
|
_selectedCriteria = result;
|
||||||
|
_criteriaValue = string.Empty;
|
||||||
|
|
||||||
|
Filter.CustomIndexFileFiltersLikeOr = AssignFilters();
|
||||||
|
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<IEnumerable<string>> FilterDocumentProcessingInfosBySearchCriteria(string search)
|
||||||
|
{
|
||||||
|
return Task.FromResult(_criteria.Keys.ToList().Where(s => s.ToString().Contains(search, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCriteriaValue(string selectedValue)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(_selectedCriteria))
|
||||||
|
{
|
||||||
|
Filter.CustomIndexFileFiltersLikeOr = AssignFilters(selectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string,string>? AssignFilters(string value = "")
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_selectedCriteria))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dictionary = new Dictionary<string, string>();
|
||||||
|
foreach (var filter in _criteria[_selectedCriteria]._filters)
|
||||||
|
{
|
||||||
|
dictionary[filter] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<div class="grid-subheader-row-container processing-header">
|
||||||
|
<div class="col">
|
||||||
|
<h7><b>@ChildContent</b></h7>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
|
||||||
|
<div class="form-row col-12">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="col-12 col-form-label">Ingestor Run Id:</label>
|
||||||
|
<InputText type="search"
|
||||||
|
id="IngestorRunInfoIdfilter"
|
||||||
|
class="form-control"
|
||||||
|
@bind-Value="_ingestorRunInfoIdString"
|
||||||
|
@oninput="IngestorRunInfoIdChanged"
|
||||||
|
placeholder="Type to filter records by Ingestor Run Id..."
|
||||||
|
spellcheck="false" autocomplete="false"/>
|
||||||
|
@if (!_ingestorInfoIdIsValid)
|
||||||
|
{
|
||||||
|
<span style="color: red;" class="pl-3">Ingestor Run Id must be a guid</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="col-6"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
private string? _ingestorRunInfoIdString = null;
|
||||||
|
private bool _ingestorInfoIdIsValid = true;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
if (Filter.IngestorRunInfoId == null) _ingestorRunInfoIdString = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IngestorRunInfoIdChanged(ChangeEventArgs changeEventArgs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(changeEventArgs.Value?.ToString()))
|
||||||
|
{
|
||||||
|
Filter.IngestorRunInfoId = null;
|
||||||
|
_ingestorRunInfoIdString = null;
|
||||||
|
}
|
||||||
|
else if (Guid.TryParse(changeEventArgs.Value?.ToString(), out var guid))
|
||||||
|
{
|
||||||
|
Filter.IngestorRunInfoId = guid;
|
||||||
|
_ingestorRunInfoIdString = Filter.IngestorRunInfoId.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Filter.IngestorRunInfoId = Guid.Empty;
|
||||||
|
_ingestorRunInfoIdString = changeEventArgs.Value?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ingestorInfoIdIsValid = Filter.IngestorRunInfoId != Guid.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="modal fade show d-block d-flex justify-content-center align-items-center" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-backdrop fade show"></div>
|
||||||
|
<div class="modal-dialog loading-lighthouse">
|
||||||
|
<img src="images/lighthouse.gif" alt="LCU Lighthouse" class="loading-lighthouse">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
public static IModalReference Show(IModalService modal)
|
||||||
|
{
|
||||||
|
var options = new ModalOptions
|
||||||
|
{
|
||||||
|
UseCustomLayout = true
|
||||||
|
};
|
||||||
|
|
||||||
|
return modal.Show<LoadingLighthouse>(string.Empty, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
div.loading-lighthouse {
|
||||||
|
z-index: 1050;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
|
||||||
|
img.loading-lighthouse {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
@using System.Text.Encodings.Web
|
||||||
|
@using Microsoft.AspNetCore.Html
|
||||||
|
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
|
@inherits ConfirmBase
|
||||||
|
|
||||||
|
@if (ShowConfirmation)
|
||||||
|
{
|
||||||
|
<div class="modal fade show d-flex" id="exampleModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog align-self-center modal-lg " role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">@ConfirmationTitle</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||||
|
@onclick="() => OnConfirmationChange(false)">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
@if (ConfirmationMessages.Any())
|
||||||
|
{
|
||||||
|
foreach (var message in ConfirmationMessages)
|
||||||
|
{
|
||||||
|
<p class="mb-2">
|
||||||
|
@((MarkupString)message)
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@ConfirmationMessage
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(WarningMessage))
|
||||||
|
{
|
||||||
|
<div class="modal-body confirm-warning">
|
||||||
|
@WarningMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal"
|
||||||
|
@onclick="() => OnConfirmationChange(false)">@CancelButtonText</button>
|
||||||
|
<button type="button" class="btn btn-danger"
|
||||||
|
@onclick="() => OnConfirmationChange(true)">@ConfirmButtonText</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.Components.Modals;
|
||||||
|
|
||||||
|
public class ConfirmBase : ComponentBase
|
||||||
|
{
|
||||||
|
protected bool ShowConfirmation { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ConfirmationTitle { get; set; } = "Confirm";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ConfirmationMessage { get; set; } = "Are you sure?";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public List<string> ConfirmationMessages { get; set; } = new();
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string WarningMessage { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string CancelButtonText { get; set; } = "Cancel";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ConfirmButtonText { get; set; } = "Confirm";
|
||||||
|
|
||||||
|
public void Show()
|
||||||
|
{
|
||||||
|
ShowConfirmation = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<bool> ConfirmationChanged { get; set; }
|
||||||
|
|
||||||
|
protected async Task OnConfirmationChange(bool value)
|
||||||
|
{
|
||||||
|
ShowConfirmation = false;
|
||||||
|
await ConfirmationChanged.InvokeAsync(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
@inherits ModalBase
|
||||||
|
|
||||||
|
@if (Visible)
|
||||||
|
{
|
||||||
|
<div class="modal fade show d-flex" id="exampleModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog align-self-center" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">@Title</h5>
|
||||||
|
<button type="button"
|
||||||
|
class="close"
|
||||||
|
data-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
@onclick="@(() => Close(false))">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
@ChildContent
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
data-dismiss="modal"
|
||||||
|
@onclick="@(() => Close(false))">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-danger"
|
||||||
|
disabled="@(!ConfirmAllowed)"
|
||||||
|
@onclick="@(() => Close(true))">
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.Components.Modals;
|
||||||
|
|
||||||
|
public class ModalBase : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<bool> OnClose { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool ConfirmAllowed { get; set; } = true;
|
||||||
|
|
||||||
|
protected bool Visible { get; set; }
|
||||||
|
|
||||||
|
public void Show()
|
||||||
|
{
|
||||||
|
Visible = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task Close(bool confirmed)
|
||||||
|
{
|
||||||
|
Visible = false;
|
||||||
|
await OnClose.InvokeAsync(confirmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
@inject IToastService ToastService;
|
||||||
|
@inject IDocumentReprocessingService QueueDocumentReprocessingServices;
|
||||||
|
@inject IDocumentProcessingInfoService DocumentProcessingInfoService;
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Confirm @ref="ReprocessCriticalFailuresConfirmation" ConfirmationChanged="ConfirmReprocessCriticalFailures_Click"
|
||||||
|
ConfirmationMessage=@("Are you sure you want to reprocess all CriticalFailures?")
|
||||||
|
WarningMessage=@_limitMessage
|
||||||
|
ConfirmationTitle=@("Reprocess Critical Failures")>
|
||||||
|
</Confirm>
|
||||||
|
<button id="reprocessAllCF"
|
||||||
|
disabled="@ReprocessAllCfDisabled"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning reprocessBtn @(ReprocessAllCfDisabled ? "btnLoading" : string.Empty)"
|
||||||
|
title="ReprocessCriticalFailures"
|
||||||
|
@onclick="ReprocessCriticalFailures_Click">
|
||||||
|
Reprocess Critical Failures
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private ConfirmBase ReprocessCriticalFailuresConfirmation { get; set; }
|
||||||
|
private bool ReprocessAllCfDisabled { get; set; } = false;
|
||||||
|
|
||||||
|
private const int FilterLimit = 1000;
|
||||||
|
private string _limitMessage = string.Empty;
|
||||||
|
|
||||||
|
private async void ReprocessCriticalFailures_Click()
|
||||||
|
{
|
||||||
|
var count = await CountCriticalFailures();
|
||||||
|
_limitMessage = count > FilterLimit
|
||||||
|
? $"Reprocess Critical Failures is limited to {FilterLimit} documents. " +
|
||||||
|
$"Remaining {count - FilterLimit} documents will not be reprocessed."
|
||||||
|
: string.Empty;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
ReprocessCriticalFailuresConfirmation.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConfirmReprocessCriticalFailures_Click(bool deleteConfirmed)
|
||||||
|
{
|
||||||
|
if (deleteConfirmed)
|
||||||
|
{
|
||||||
|
ReprocessAllCfDisabled = true;
|
||||||
|
await ReprocessCriticalFailures();
|
||||||
|
ReprocessAllCfDisabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReprocessCriticalFailures()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ToastService.ShowInfo("Queueing CriticalFailure documents for reprocessing...");
|
||||||
|
await QueueDocumentReprocessingServices.QueueDocumentReprocessingAllCriticalFailures();
|
||||||
|
ToastService.ShowInfo("CriticalFailure documents are set for re-processing.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> CountCriticalFailures()
|
||||||
|
{
|
||||||
|
var allCriticalFailureFilter = new Filter()
|
||||||
|
{
|
||||||
|
Page = 1,
|
||||||
|
Limit = 1,
|
||||||
|
StatusFilters = new() { new() { IsEnabled = true, IsUnknown = false, StatusType = ProcessStatusTypes.CriticalFailure}}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (await DocumentProcessingInfoService.GetFilteredDocumentProcessingInfos(allCriticalFailureFilter)).TotalItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
@inject IToastService ToastService;
|
||||||
|
@inject IDocumentReprocessingService DocumentReprocessingService;
|
||||||
|
@inject IDocumentProcessingInfoService DocumentProcessingInfoService;
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Confirm @ref="ReprocessConfirmation" ConfirmationChanged="OnConfirm"
|
||||||
|
ConfirmationMessage=@($"Are you sure you want to reprocess all {StatusName}s?")
|
||||||
|
WarningMessage=@_limitMessage
|
||||||
|
ConfirmationTitle=@("Reprocess Notification Data Failures")>
|
||||||
|
</Confirm>
|
||||||
|
<button
|
||||||
|
disabled="@Disabled"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning reprocessBtn @(Disabled ? "btnLoading" : string.Empty)"
|
||||||
|
title="ReprocessNotificationDataFailures"
|
||||||
|
@onclick="OnClick">
|
||||||
|
Reprocess Notification Data Failures
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private const string StatusName = nameof(ProcessStatusTypes.NotificationDataFailure);
|
||||||
|
private ConfirmBase ReprocessConfirmation { get; set; } = null!;
|
||||||
|
private bool Disabled { get; set; } = false;
|
||||||
|
|
||||||
|
private const int FilterLimit = 1000;
|
||||||
|
private string _limitMessage = string.Empty;
|
||||||
|
|
||||||
|
private async void OnClick()
|
||||||
|
{
|
||||||
|
var count = await CountNotificationFailures();
|
||||||
|
_limitMessage = count > FilterLimit
|
||||||
|
? $"Reprocess Notification Data Failures is limited to {FilterLimit} records. " +
|
||||||
|
$"Remaining {count - FilterLimit} records will not be reprocessed."
|
||||||
|
: string.Empty;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
ReprocessConfirmation.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnConfirm(bool confirmed)
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
Disabled = true;
|
||||||
|
await Reprocess();
|
||||||
|
Disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Reprocess()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ToastService.ShowInfo($"Queueing {StatusName} documents for reprocessing...");
|
||||||
|
await DocumentReprocessingService.ReprocessAllNotificationDataFailures();
|
||||||
|
ToastService.ShowInfo($"{StatusName} documents are set for reprocessing.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> CountNotificationFailures()
|
||||||
|
{
|
||||||
|
var allNotificationFailureFilter = new Filter()
|
||||||
|
{
|
||||||
|
Page = 1,
|
||||||
|
Limit = 1,
|
||||||
|
StatusFilters = new() { new() { IsEnabled = true, IsUnknown = false, StatusType = ProcessStatusTypes.NotificationDataFailure}}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (await DocumentProcessingInfoService.GetFilteredDocumentProcessingInfos(allNotificationFailureFilter)).TotalItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
@using Serilog
|
||||||
|
@inject AppConfiguration AppConfiguration;
|
||||||
|
@inject IToastService ToastService;
|
||||||
|
@inject IDocumentProcessingInfoService DocumentProcessingInfoService;
|
||||||
|
@inject IJSTooltips JSTooltips;
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Confirm @ref="SetEntityToUnknownConfirmation" ConfirmationChanged="ConfirmSetEntitiesToUnknown_Click"
|
||||||
|
ConfirmationMessage=@($"Are you sure you want to set Entity to Unknown for {ViewModel.TotalItems} documents?")
|
||||||
|
WarningMessage=@_limitMessage
|
||||||
|
ConfirmationTitle=@("Set Entity to Unknown")>
|
||||||
|
</Confirm>
|
||||||
|
<button id=""
|
||||||
|
disabled="@(SetEntityToUnknownDisabled())"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning reprocessBtn @(_loading ? "btnLoading" : string.Empty)"
|
||||||
|
title="Set Entities to Unknown"
|
||||||
|
@onclick="SetEntityToUnknown_Click">
|
||||||
|
Unknown @if (!SetEntityToUnknownDisabled())
|
||||||
|
{
|
||||||
|
@("(" + ViewModel.TotalItems + " documents)")
|
||||||
|
}
|
||||||
|
<i class="fa fa-info-circle @(SetEntityToUnknownDisabled() ? "" : "collapse")" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="Enabled when filter has values for Doc Ingestion Dates (Start and End), Doc Business Name, and Process Status is set to ONLY DataMappingFailure."></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public ITablePagingViewModel<DocumentProcessingInfo, Filter> ViewModel { get; set; }
|
||||||
|
|
||||||
|
private ConfirmBase? SetEntityToUnknownConfirmation { get; set; }
|
||||||
|
|
||||||
|
private const int FilterLimit = 1000;
|
||||||
|
private string _limitMessage = string.Empty;
|
||||||
|
private bool _loading = false;
|
||||||
|
private bool SetEntityToUnknownDisabled()
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(ViewModel.Filter.DocBusinessName) ||
|
||||||
|
ViewModel.Filter.CreatedDateStart == null ||
|
||||||
|
ViewModel.Filter.CreatedDateEnd == null ||
|
||||||
|
ViewModel.Filter.StatusFilters.Count() != 1 ||
|
||||||
|
ViewModel.Filter.StatusFilters.Count(x => x is {StatusType: ProcessStatusTypes.DataMappingFailure, IsUnknown: false, IsEnabled: true }) != 1 ||
|
||||||
|
ViewModel.TotalItems == 0 ||
|
||||||
|
_loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TotalItemsExceedsLimit() => ViewModel.TotalItems > FilterLimit;
|
||||||
|
|
||||||
|
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if(firstRender) JSTooltips.Enable();
|
||||||
|
return base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetEntityToUnknown_Click()
|
||||||
|
{
|
||||||
|
_limitMessage = TotalItemsExceedsLimit()
|
||||||
|
? $"Setting Entity to Unknown is limited to {FilterLimit} documents. " +
|
||||||
|
$"Remaining {ViewModel.TotalItems - FilterLimit} documents will not be processed."
|
||||||
|
: string.Empty;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
SetEntityToUnknownConfirmation?.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConfirmSetEntitiesToUnknown_Click(bool setEntityConfirmed)
|
||||||
|
{
|
||||||
|
if (setEntityConfirmed)
|
||||||
|
{
|
||||||
|
await SetEntityToUnknown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetEntityToUnknown()
|
||||||
|
{
|
||||||
|
var totalItems = ViewModel.TotalItems < FilterLimit ? ViewModel.TotalItems : FilterLimit;
|
||||||
|
ToastService.ShowInfo($"Setting Entity to Unknown for {totalItems} documents.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_loading = true;
|
||||||
|
await DocumentProcessingInfoService.SetDocumentsToEntityUnknownByFilter(ViewModel.Filter, totalItems);
|
||||||
|
_loading = false;
|
||||||
|
ToastService.ShowInfo($"Entities have been set to Unknown for {totalItems} documents.");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
await ViewModel.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
@using LCU.Library.Internal.FileOperationsModels.Shared
|
||||||
|
@inject IJSTooltips JSTooltips;
|
||||||
|
@inject IDocumentProcessingInfoService DocumentProcessingInfoService;
|
||||||
|
@inject IModalService _modal;
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Modal @ref="ChangeNotificationModal"
|
||||||
|
OnClose="@ShowConfirmation"
|
||||||
|
ConfirmAllowed="@ConfirmAllowed"
|
||||||
|
Title="Set Notification Status">
|
||||||
|
<div>
|
||||||
|
<label class="col-form-label text-nowrap required" for="select-status">Notification Status:</label>
|
||||||
|
<select id="select-status" class="form-control" required @bind="@_pendingChange.Status">
|
||||||
|
<option/>
|
||||||
|
@foreach (var value in Enum.GetValues<NotificationStatusTypes>())
|
||||||
|
{
|
||||||
|
<option value="@value">@value.ToString()</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="col-form-label text-nowrap required" for="change-reason">Change Reason:</label>
|
||||||
|
<input id="change-reason" class="form-control" type="text" required value="@_pendingChange.ChangeReason" @oninput="@(change => _pendingChange.ChangeReason = change.Value?.ToString() ?? string.Empty)"/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<Confirm @ref="Confirmation"
|
||||||
|
ConfirmationMessage="@($"Update {_potentialUpdates} to {_pendingChange.Status} because {_pendingChange.ChangeReason}?")"
|
||||||
|
ConfirmationChanged="@OnConfirm"/>
|
||||||
|
<button disabled="@(Disabled || _potentialUpdates == 0)"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning reprocessBtn @(_updating ? "btnLoading" : string.Empty)"
|
||||||
|
title="Batch Update Notification Status"
|
||||||
|
@onclick="@BatchSetNotificationStatus_Click">
|
||||||
|
Update Notification Status
|
||||||
|
@if (!DisabledByFilter)
|
||||||
|
{
|
||||||
|
@($"({_potentialUpdates} documents)")
|
||||||
|
}
|
||||||
|
<i class="fa fa-info-circle @(DisabledByFilter ? "" : "collapse")" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="Enabled when the filter has a valid Document Ingestion ID."></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Filter Filter { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<IModalReference> RefreshData { get; set; }
|
||||||
|
|
||||||
|
private bool Disabled => _updating || DisabledByFilter;
|
||||||
|
private bool DisabledByFilter => Filter?.IngestorRunInfoId is null || Filter.IngestorRunInfoId == Guid.Empty;
|
||||||
|
private bool ConfirmAllowed => _pendingChange.Status != null && !string.IsNullOrWhiteSpace(_pendingChange.ChangeReason);
|
||||||
|
|
||||||
|
private Modal ChangeNotificationModal { get; set; } = null!;
|
||||||
|
private Confirm Confirmation { get; set; } = null!;
|
||||||
|
private IModalReference openModal;
|
||||||
|
|
||||||
|
private Filter _filter = new();
|
||||||
|
private int? _potentialUpdates;
|
||||||
|
private bool _updating = false;
|
||||||
|
private NotificationStatusChange _pendingChange = DefaultChangeFactory();
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) await JSTooltips.Enable();
|
||||||
|
|
||||||
|
if (!Disabled && !AreEqual(_filter, Filter))
|
||||||
|
{
|
||||||
|
_filter = Filter.DeepClone();
|
||||||
|
if (_potentialUpdates != (_potentialUpdates = await CountStatusUpdates()))
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> CountStatusUpdates()
|
||||||
|
{
|
||||||
|
var allNotificationStatusFilter = Filter.DeepClone();
|
||||||
|
allNotificationStatusFilter.Page = 1;
|
||||||
|
allNotificationStatusFilter.Limit = 1;
|
||||||
|
if (allNotificationStatusFilter.NotificationStatusFilters is null or { Count: 0 })
|
||||||
|
{
|
||||||
|
allNotificationStatusFilter.NotificationStatusFilters = Enum.GetValues<NotificationStatusTypes>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await DocumentProcessingInfoService.GetFilteredDocumentProcessingInfos(allNotificationStatusFilter)).TotalItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NotificationStatusChange DefaultChangeFactory()
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Status = null,
|
||||||
|
ChangeReason = string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
private void BatchSetNotificationStatus_Click()
|
||||||
|
{
|
||||||
|
ChangeNotificationModal.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowConfirmation(bool confirmed)
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
Confirmation.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pendingChange = DefaultChangeFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnConfirm(bool confirmed)
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
openModal = LoadingLighthouse.Show(_modal);
|
||||||
|
_updating = true;
|
||||||
|
await DocumentProcessingInfoService.BulkSetDocumentNotificationStatus(Filter, _pendingChange.Status.Value, _pendingChange.ChangeReason);
|
||||||
|
_updating = false;
|
||||||
|
await RefreshData.InvokeAsync(openModal);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ }
|
||||||
|
|
||||||
|
_pendingChange = DefaultChangeFactory();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChangeNotificationModal.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record NotificationStatusChange
|
||||||
|
{
|
||||||
|
public NotificationStatusTypes? Status { get; set; }
|
||||||
|
public string ChangeReason { get; set; }
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool AreEqual<T>(T object1, T object2)
|
||||||
|
{
|
||||||
|
var obj1 = JsonConvert.SerializeObject(object1);
|
||||||
|
var obj2 = JsonConvert.SerializeObject(object2);
|
||||||
|
|
||||||
|
var compare = string.Compare(obj1, obj2, StringComparison.OrdinalIgnoreCase);
|
||||||
|
return compare == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
@ChildContent((Working, ExecuteAction))
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Func<Task<bool>> Action { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Action? OnSuccess { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<(bool working, Func<Task> Action)> ChildContent { get; set; }
|
||||||
|
|
||||||
|
private readonly SemaphoreSlim _otherActions = new (1);
|
||||||
|
private bool Working => _otherActions.CurrentCount == 0;
|
||||||
|
|
||||||
|
private async Task ExecuteAction()
|
||||||
|
{
|
||||||
|
_otherActions.WaitAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var succeeded = await Action();
|
||||||
|
|
||||||
|
if (succeeded && OnSuccess is {} success) success();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_otherActions.Release();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5 ml-3">
|
||||||
|
<b>@Label:</b>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-7">
|
||||||
|
@AttributeValue
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string Label { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string AttributeValue { get; set; }
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@using Serilog
|
||||||
|
@inject IJSRuntime JSRuntime;
|
||||||
|
@inject IToastService ToastService;
|
||||||
|
@inject FileOperationsApiConfiguration _fileOperationsApiConfiguration;
|
||||||
|
@inject AuthenticationHelper Auth;
|
||||||
|
|
||||||
|
<RowSubHeader>Document Attributes</RowSubHeader>
|
||||||
|
<div class="pt-2 pl-3">
|
||||||
|
@switch (DocumentProcessingInfo.IngestorRunInfo.IngestorType)
|
||||||
|
{
|
||||||
|
case IngestorTypes.FosTaxStatement:
|
||||||
|
<FosTaxStatementComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.FosDnaDocument:
|
||||||
|
<FosDnaDocumentComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.XdiDnaNotice:
|
||||||
|
<XdiDnaNoticeComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.FosLoanStatement:
|
||||||
|
<FosLoanStatementDocumentComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.OsgDnaNoticesDocument:
|
||||||
|
<OsgDnaNoticesDocumentComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.OsgHistoricalDocument:
|
||||||
|
<OsgHistoricalDocumentComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
case IngestorTypes.OsgWeilandStatement:
|
||||||
|
<OsgWeilandStatementComponent DocumentProcessingInfo=DocumentProcessingInfo />
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="row justify-content-center mt-4 mb-2">
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="(() => OpenDocument())" title="OpenDocument" disabled="@Disabled">
|
||||||
|
Open Document
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
|
||||||
|
private bool Disabled => !Auth.PolicySatisfied(PolicyConstants.ReprocessingTableOpenDocument);
|
||||||
|
|
||||||
|
private async Task OpenDocument()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filePath = DocumentProcessingInfo.IngestorRunInfo.IndexFilePath + DocumentProcessingInfo.DocumentFileName;
|
||||||
|
var finalFilePath = filePath.Replace("\\", "%5C");
|
||||||
|
var url = $"{_fileOperationsApiConfiguration.Url}" +
|
||||||
|
$"{_fileOperationsApiConfiguration.GetPdfContentEndpoint}" +
|
||||||
|
$"/{finalFilePath}";
|
||||||
|
|
||||||
|
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var message = "Error opening document.";
|
||||||
|
Log.Error(message, exception);
|
||||||
|
ToastService.ShowError(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
<AttributeFieldComponent Label=@AccountLabel AttributeValue=@Account />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 ml-3">
|
||||||
|
<b>@MemberNumberOrUserIdLabel:</b>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-7">
|
||||||
|
@MemberNumberOrUserId
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string Account { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string MemberNumberOrUserId { get; set; }
|
||||||
|
private string AccountLabel = "Account Number";
|
||||||
|
private string MemberNumberOrUserIdLabel = "Member Number Or User Id";
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
Account = string.IsNullOrWhiteSpace(Account) ? "N/A" : Account;
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
<AttributeFieldComponent Label=@AccountLabel AttributeValue=@Account />
|
||||||
|
<AttributeFieldComponent Label=@NameLabel AttributeValue=@Name />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string Account { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string Name { get; set; }
|
||||||
|
private string AccountLabel = "Account Number";
|
||||||
|
private string NameLabel = "Entity Name";
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
Account = string.IsNullOrWhiteSpace(Account) ? "N/A" : Account;
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
<AttributeFieldComponent Label=@NameLabel AttributeValue=@Name />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 ml-3">
|
||||||
|
<b>@MemberNumberOrUserIdLabel:</b>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-7">
|
||||||
|
@MemberNumberOrUserId
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string MemberNumberOrUserId { get; set; }
|
||||||
|
private string NameLabel = "Entity Name";
|
||||||
|
private string MemberNumberOrUserIdLabel = "Member Number Or User Id";
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<AttributeFieldComponent Label=@TinLabel AttributeValue=@Tin />
|
||||||
|
<AttributeFieldComponent Label=@NameLabel AttributeValue=@Name />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string Tin { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string Name { get; set; }
|
||||||
|
private string TinLabel = "Entity Tin";
|
||||||
|
private string NameLabel = "Entity Name";
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityAccountAndNameComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private FosDnaDocument DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<FosDnaDocument>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityAccountAndNameComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private FosLoanStatement DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<FosLoanStatement>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityTinAndNameComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private FosTaxStatementDocument DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<FosTaxStatementDocument>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityAccountAndMemberNumberOrUserIdComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private OsgDnaNoticesDocument DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<OsgDnaNoticesDocument>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<EntityNameAndMemberNumberOrUserIdComponent
|
||||||
|
[email protected]
|
||||||
|
[email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private OsgHistoricalDocument DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<OsgHistoricalDocument>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityAccountAndNameComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private OsgWeilandStatement DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<OsgWeilandStatement>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<EntityAccountAndNameComponent [email protected] [email protected] />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
private XdiDnaNoticesDocument DocumentValuePayload { get; set; }
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
DocumentValuePayload = JsonConvert.DeserializeObject<XdiDnaNoticesDocument>(DocumentProcessingInfo.DocumentValuePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView.DocumentProcessingRowBodies;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
public class BusinessReprocessingRowBody : DocumentProcessingRowBody
|
||||||
|
{
|
||||||
|
protected override Type BodyType => DocumentProcessingInfo.StatusType switch
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.DataMappingFailure => typeof(BusinessDataMappingFailure),
|
||||||
|
_ => base.BodyType
|
||||||
|
};
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
@using LCU.Library.Internal.DocumentModels.Responses
|
||||||
|
@using JsonSerializer = System.Text.Json.JsonSerializer
|
||||||
|
@inject IJSDate JSDate;
|
||||||
|
|
||||||
|
<CollapsibleComponent ButtonClass="btn btn-link"
|
||||||
|
HeaderClass="row grid-header-row grid-subheader-row-container processing-header"
|
||||||
|
ButtonContainerClass="grid-button-container"
|
||||||
|
IsCollapsed="false">
|
||||||
|
<HeaderContent>
|
||||||
|
<RowSubHeader>Audit Log</RowSubHeader>
|
||||||
|
</HeaderContent>
|
||||||
|
<BodyContent>
|
||||||
|
<div class="log-message-container overflow-auto">
|
||||||
|
@if (AuditTrail is not null && AuditTrail.Any())
|
||||||
|
{
|
||||||
|
foreach (var log in AuditTrail)
|
||||||
|
{
|
||||||
|
<div class="d-flex border-bottom">
|
||||||
|
<div class="col-3 col-with-right-boarder d-flex flex-column">
|
||||||
|
<span>
|
||||||
|
Audit Timestamp
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
@_localTimes?[log]
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 col-with-right-boarder d-flex flex-column">
|
||||||
|
<span>
|
||||||
|
User
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
@log.UserId
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col col-with-right-boarder d-flex flex-column">
|
||||||
|
<span>
|
||||||
|
Audit Message
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
@FormatLogMessage(log)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="col-12 col-with-right-boarder grid-subheader-row-container" style="border-top: 1px solid rgba(0, 0, 0, .125);">
|
||||||
|
<span>
|
||||||
|
No Audit Log information found...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</BodyContent>
|
||||||
|
</CollapsibleComponent>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<DocumentAuditResponse> AuditTrail { get; set; }
|
||||||
|
|
||||||
|
private IDictionary<DocumentAuditResponse, string> _localTimes;
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
if(AuditTrail != null) {
|
||||||
|
_localTimes = (await Task.WhenAll(AuditTrail.Select(async a => (response: a, time: await JSDate.AsClientLocalTime(a.Date)))))
|
||||||
|
.ToDictionary(t => t.response, t => t.time);
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatLogMessage(DocumentAuditResponse auditLog) => auditLog switch
|
||||||
|
{
|
||||||
|
DocumentActionAuditResponse log => log.ActionDescription,
|
||||||
|
DocumentFieldAuditResponse log => $"{log.Field} was changed from " +
|
||||||
|
JsonSerializer.Serialize(log.PreviousValue != null ? log.PreviousValue : "null") +
|
||||||
|
$" to {JsonSerializer.Serialize(log.CurrentValue != null ? log.CurrentValue : "null")}",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(auditLog))
|
||||||
|
};
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
@inject IJSDate JSDate;
|
||||||
|
|
||||||
|
<div class="col-3 col-with-right-boarder">
|
||||||
|
<div>
|
||||||
|
Created Date
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6>@Document.CreatedDate</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 col-with-right-boarder">
|
||||||
|
<div>
|
||||||
|
Document Type
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6>@Document.DocumentType</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-with-right-boarder">
|
||||||
|
<div>
|
||||||
|
Document Business Name
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6>@Document.DocumentBusinessName</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-with-right-boarder">
|
||||||
|
<div>
|
||||||
|
Entity Name
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6>@Document.EntityName</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public DocumentInformation Document { get; set; }
|
||||||
|
|
||||||
|
private string _localCreatedDate = string.Empty;
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
_localCreatedDate = await JSDate.AsClientLocalTime(Document.CreatedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
using LCU.Library.Internal.DocumentModels.Responses;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
public record BusinessViewDocumentInformation
|
||||||
|
(
|
||||||
|
DocumentInformation DocumentInformation,
|
||||||
|
IEnumerable<DocumentAuditResponse> Logs
|
||||||
|
);
|
||||||
|
|
||||||
|
public record DocumentInformation(
|
||||||
|
Guid DocumentId,
|
||||||
|
Guid? TraceId,
|
||||||
|
string Source,
|
||||||
|
string DataSource,
|
||||||
|
DateTime CreatedDate,
|
||||||
|
DateTime? UploadDate,
|
||||||
|
string Name,
|
||||||
|
string DocumentType,
|
||||||
|
string DocumentCode,
|
||||||
|
string DocumentBusinessName,
|
||||||
|
string DocumentMemberFacingName,
|
||||||
|
int OnlineBankingRetentionMonths,
|
||||||
|
int InternalRetentionMonths,
|
||||||
|
string EntityName,
|
||||||
|
Dictionary<string, Dictionary<string, string>> AccountOwners,
|
||||||
|
ObservableCollection<DocumentAccess> DocumentAccess,
|
||||||
|
ObservableCollection<string> PersonNumbers,
|
||||||
|
ObservableCollection<string> OrganizationNumbers,
|
||||||
|
long? AccountNumber,
|
||||||
|
bool TypeDisabled,
|
||||||
|
bool? ScanStatus,
|
||||||
|
Func<Task> Download
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//assume that documents that were uploaded 2 hours or more ago have been scanned
|
||||||
|
//"safe enough" given that
|
||||||
|
// - no real documents have set off Azure Defender
|
||||||
|
// - test malicious documents took an average of 1h:45m to be scanned
|
||||||
|
// - Azure Defender isn't integrated (yet) for document scan completion observation
|
||||||
|
public bool Scanned => UploadDate.Value.AddHours(2) <= DateTime.UtcNow;
|
||||||
|
};
|
||||||
|
|
||||||
|
public enum AccessType
|
||||||
|
{
|
||||||
|
PrimaryOwner,
|
||||||
|
Owner,
|
||||||
|
Additional,
|
||||||
|
Removed
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DocumentAccess(
|
||||||
|
AccessType AccessType,
|
||||||
|
bool HasAccess,
|
||||||
|
string EntityName,
|
||||||
|
string EntityId
|
||||||
|
);
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="col-3 col-with-right-boarder p-0">
|
||||||
|
<DocumentAttributes
|
||||||
|
CustomerFacingName="@Document.DocumentMemberFacingName"
|
||||||
|
AccountNumber="@Document.AccountNumber?.ToString()"
|
||||||
|
EntityName="@Document.EntityName"
|
||||||
|
VisibleInMemberView="@(!Document.TypeDisabled)"
|
||||||
|
DownloadDocument="@Document.Download"
|
||||||
|
CreatedDate="@Document.CreatedDate"
|
||||||
|
OnlineBankingRetentionMonths="@Document.OnlineBankingRetentionMonths"
|
||||||
|
InternalRetentionMonths="@Document.InternalRetentionMonths"
|
||||||
|
DocumentId="@Document.DocumentId.ToString()"
|
||||||
|
ScanStatus="@Document.ScanStatus"
|
||||||
|
Scanned="@Document.Scanned"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 p-0">
|
||||||
|
<div class="col-12 p-0">
|
||||||
|
<DocumentAuthorizationsContainer Document="@Document"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 p-0">
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.DocumentLookupEditVisibility">
|
||||||
|
<DocumentVisibilityComponent Document="@Document" DocumentAuthorization="@ViewModel"/>
|
||||||
|
</AuthorizeView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public DocumentInformation Document { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public IBusinessViewViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
using DocumentTypeRetriever;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Enums;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
public record BusinessViewFilter
|
||||||
|
{
|
||||||
|
public OwnershipTypes? OwnershipType { get; set; } = null;
|
||||||
|
public string OwnershipValue { get; set; } = string.Empty;
|
||||||
|
public string AdditionalOwnershipValue { get; set; } = string.Empty;
|
||||||
|
public DocumentInformationFilteredSearch DocumentProcessingFilter { get; set; } = new()
|
||||||
|
{
|
||||||
|
Filter = new DocumentInformationFilter(),
|
||||||
|
SortDefinition = new List<SortableField>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Field = "CreatedDate",
|
||||||
|
IsDescending = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
+908
@@ -0,0 +1,908 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Blazored.Modal.Services;
|
||||||
|
using Blazored.Toast.Services;
|
||||||
|
using DocumentTypeRetriever;
|
||||||
|
using LCU.Library.DocumentType.Enums;
|
||||||
|
using LCU.Library.DocumentType.Extensions;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Accounts;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Organizations;
|
||||||
|
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Persons;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Enums;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Models;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Configurations;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Services;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.Components;
|
||||||
|
using LCU.WebApps.Internal.DocumentManager.Shared.JSInterop;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
using Serilog;
|
||||||
|
using DocumentAction = LCU.Library.Internal.DocumentModels.Requests.DocumentAction;
|
||||||
|
using DocumentAuditResponse = LCU.Library.Internal.DocumentModels.Responses.DocumentAuditResponse;
|
||||||
|
using DocumentMetadataResponse = LCU.Library.DocumentType.Models.Responses.External.DocumentMetadataResponse;
|
||||||
|
|
||||||
|
namespace LCU.WebApps.Internal.DocumentManager.Shared.DocumentGridView;
|
||||||
|
|
||||||
|
public interface IBusinessViewViewModel : ITablePagingViewModel<BusinessViewDocumentInformation, BusinessViewFilter>, IDocumentAuthorization
|
||||||
|
{
|
||||||
|
event EventHandler DataLoadError;
|
||||||
|
void FilterChanged(OwnershipTypes? ownership, string field, string value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BusinessViewViewModel : IBusinessViewViewModel
|
||||||
|
{
|
||||||
|
public bool Loaded { get; private set; } = false;
|
||||||
|
public BusinessViewFilter Filter { get; } = new();
|
||||||
|
|
||||||
|
public bool FilterIsValid { get; set; } = true;
|
||||||
|
public event EventHandler? OnFilterValidityChanged = delegate { };
|
||||||
|
public void FilterChanged(OwnershipTypes? ownership, string field, string value)
|
||||||
|
{
|
||||||
|
FilterIsValid = ownership switch
|
||||||
|
{
|
||||||
|
null => true,
|
||||||
|
OwnershipTypes.AccountNumber or OwnershipTypes.MemberNumber or OwnershipTypes.OrganizationNumber or OwnershipTypes.PersonNumber
|
||||||
|
=> float.TryParse(value, out _),
|
||||||
|
OwnershipTypes.PersonName when field is nameof(Filter.OwnershipType)
|
||||||
|
=> !(string.IsNullOrWhiteSpace(value) && string.IsNullOrWhiteSpace(Filter.AdditionalOwnershipValue)),
|
||||||
|
OwnershipTypes.PersonName when field is nameof(Filter.AdditionalOwnershipValue)
|
||||||
|
=> !(string.IsNullOrWhiteSpace(Filter.OwnershipValue) && string.IsNullOrWhiteSpace(value)),
|
||||||
|
OwnershipTypes.OrganizationName
|
||||||
|
=> !string.IsNullOrWhiteSpace(value),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<BusinessViewDocumentInformation> VisibleItems { get; } = new();
|
||||||
|
public event EventHandler DataChanged = delegate { };
|
||||||
|
public event EventHandler DataLoadError = delegate { };
|
||||||
|
|
||||||
|
public int ItemsPerPage { get; private set; } = 10;
|
||||||
|
public IReadOnlyList<int> PossibleItemsPerPage { get; } = new[] { 10, 25, 50 };
|
||||||
|
public int CurrentPage { get; private set; } = 1;
|
||||||
|
public int TotalPages => (int)Math.Ceiling((double)TotalItems / ItemsPerPage);
|
||||||
|
public int TotalItems { get; private set; }
|
||||||
|
public bool NoPriorPage => CurrentPage <= 1;
|
||||||
|
public bool NoNextPage => CurrentPage >= TotalPages;
|
||||||
|
public int VisibleItemsCount { get; private set; }
|
||||||
|
public IList<string> SelectedSortableFields => Filter.DocumentProcessingFilter.SortDefinition!.Select(f => f.Field).ToList();
|
||||||
|
|
||||||
|
private readonly string[] _sortableFields = { "CreatedDate" };
|
||||||
|
public IReadOnlyList<string> AvailableSortableFields => _sortableFields
|
||||||
|
.Except(SelectedSortableFields)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
private delegate object GetBusinessViewDocumentInformationValue(BusinessViewDocumentInformation details);
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, GetBusinessViewDocumentInformationValue> SortableValues =
|
||||||
|
new Dictionary<string, GetBusinessViewDocumentInformationValue>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"CreatedDate",
|
||||||
|
details => details.DocumentInformation.CreatedDate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DocumentType",
|
||||||
|
details => details.DocumentInformation.DocumentType
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DocumentBusinessName",
|
||||||
|
details => details.DocumentInformation.DocumentBusinessName
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EntityName",
|
||||||
|
details => details.DocumentInformation.EntityName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private IDictionary<Guid, DocumentMetadataResponse> _visibleDocuments = new Dictionary<Guid, DocumentMetadataResponse>();
|
||||||
|
private readonly IModalService _modal;
|
||||||
|
private readonly IDocumentService _documentService;
|
||||||
|
private readonly IEntityService _entityService;
|
||||||
|
private readonly IDocumentTypeService _documentTypeService;
|
||||||
|
private readonly IJSDocument _jsDocument;
|
||||||
|
private readonly IDocumentTypeUploaderService _documentTypeUploaderService;
|
||||||
|
private readonly IToastService _toastService;
|
||||||
|
private readonly IEntityService _coreApiWrapperService;
|
||||||
|
private readonly IDocumentProcessingInfoService _documentProcessingInfoService;
|
||||||
|
|
||||||
|
public BusinessViewViewModel
|
||||||
|
(
|
||||||
|
IModalService modal,
|
||||||
|
IEntityService entityService,
|
||||||
|
IDocumentService documentService,
|
||||||
|
IDocumentTypeService documentTypeService,
|
||||||
|
IDocumentTypeUploaderService documentTypeUploaderService,
|
||||||
|
IEntityService coreApiWrapperService,
|
||||||
|
IToastService toastService,
|
||||||
|
IJSDocument jsDocument,
|
||||||
|
AppConfiguration configuration,
|
||||||
|
IDocumentProcessingInfoService documentProcessingInfoService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_modal = modal;
|
||||||
|
_entityService = entityService;
|
||||||
|
_documentService = documentService;
|
||||||
|
_documentTypeService = documentTypeService;
|
||||||
|
_coreApiWrapperService = coreApiWrapperService;
|
||||||
|
_toastService = toastService;
|
||||||
|
_jsDocument = jsDocument;
|
||||||
|
_documentProcessingInfoService = documentProcessingInfoService;
|
||||||
|
_documentTypeUploaderService = documentTypeUploaderService;
|
||||||
|
|
||||||
|
(
|
||||||
|
Filter.DocumentProcessingFilter.Filter.CreatedDateMinimum,
|
||||||
|
Filter.DocumentProcessingFilter.Filter.CreatedDateMaximum
|
||||||
|
) = DefaultFilterDateRange(configuration.DefaultFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GoToPage(int page)
|
||||||
|
{
|
||||||
|
var loading = LoadingLighthouse.Show(_modal);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var documentTypes = _documentTypeService.GetDocumentTypes()
|
||||||
|
.ContinueWith(r => r.Result.ToDictionary(t => t.DocumentCode));
|
||||||
|
|
||||||
|
CurrentPage = page;
|
||||||
|
Filter.DocumentProcessingFilter.Skip = (CurrentPage - 1) * ItemsPerPage;
|
||||||
|
Filter.DocumentProcessingFilter.Take = ItemsPerPage;
|
||||||
|
_visibleDocuments = (await _documentService.GetDocuments(Filter.DocumentProcessingFilter)).ToDictionary(d => d.DocumentId);
|
||||||
|
|
||||||
|
VisibleItemsCount = _visibleDocuments.Count;
|
||||||
|
|
||||||
|
var logs = _documentService
|
||||||
|
.GetDocumentLogs(_visibleDocuments.Keys.ToArray())
|
||||||
|
.ContinueWith(response => response.Result.GroupBy(l => l.DocumentId).ToDictionary(g => g.Key));
|
||||||
|
|
||||||
|
var awaitingDocuments = _visibleDocuments.Values.Select(async document =>
|
||||||
|
{
|
||||||
|
var accountOwnerships = document.AccountNumber is {} accountNumber
|
||||||
|
? (await _entityService.GetOwnersByAccountNumber(accountNumber, document.CreatedDate)).ToList()
|
||||||
|
: await DetermineOwnerByPersonOrOrganizationNumber(document);
|
||||||
|
|
||||||
|
var primaryAccountOwnership = GetPrimaryOwner(accountOwnerships);
|
||||||
|
var entityName = GetPrimaryOwnerName(primaryAccountOwnership);
|
||||||
|
|
||||||
|
var entityHasAccess = DoesPrimaryOwnerHaveAccess(primaryAccountOwnership, document.PersonNumbers, document.OrganizationNumbers);
|
||||||
|
|
||||||
|
var accountOwners = GetAccountOwners(accountOwnerships);
|
||||||
|
|
||||||
|
var personNumbers = new ObservableCollection<string>(document.PersonNumbers ?? Array.Empty<string>());
|
||||||
|
var organizationNumbers = new ObservableCollection<string>(document.OrganizationNumbers ?? Array.Empty<string>());
|
||||||
|
|
||||||
|
var documentAccess = new ObservableCollection<DocumentAccess>();
|
||||||
|
|
||||||
|
if (primaryAccountOwnership != null)
|
||||||
|
{
|
||||||
|
documentAccess.Add(new DocumentAccess(AccessType.PrimaryOwner, entityHasAccess, entityName,
|
||||||
|
primaryAccountOwnership.ByOrgNbr == 0
|
||||||
|
? primaryAccountOwnership.PersonNumber.ToString()
|
||||||
|
: primaryAccountOwnership.ByOrgNbr.ToString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
documentAccess.Add(new DocumentAccess(AccessType.PrimaryOwner, true, entityName, "0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var documentPersonNumbers = document.PersonNumbers?.Where(p => p is not null).ToArray() ?? Array.Empty<string>();
|
||||||
|
var documentOrganizationNumbers = document.OrganizationNumbers?.Where(o => o is not null).ToArray() ?? Array.Empty<string>();
|
||||||
|
|
||||||
|
foreach (var access in GetOwnerAccess(documentPersonNumbers, documentOrganizationNumbers,
|
||||||
|
accountOwners, entityName))
|
||||||
|
{
|
||||||
|
documentAccess.Add(access);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var access in await GetAdditionalAccess(documentPersonNumbers, documentOrganizationNumbers, accountOwners))
|
||||||
|
{
|
||||||
|
documentAccess.Add(access);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var access in GetRemovedAccess(documentPersonNumbers, documentOrganizationNumbers, accountOwners, entityName))
|
||||||
|
{
|
||||||
|
documentAccess.Add(access);
|
||||||
|
}
|
||||||
|
|
||||||
|
(await documentTypes).TryGetValue(document.Type ?? string.Empty, out var documentType);
|
||||||
|
|
||||||
|
var info = new DocumentInformation
|
||||||
|
(
|
||||||
|
document.DocumentId,
|
||||||
|
document.TraceId,
|
||||||
|
document.Source,
|
||||||
|
document.DataSource,
|
||||||
|
document.CreatedDate,
|
||||||
|
document.UploadDate,
|
||||||
|
document.Name,
|
||||||
|
documentType?.DocumentType.ToString() ?? "unknown",
|
||||||
|
documentType?.DocumentCode ?? "unknown",
|
||||||
|
documentType?.DocumentBusinessName ?? "unknown",
|
||||||
|
documentType?.DocumentMemberFacingName ?? "unknown",
|
||||||
|
documentType?.OnlineBankingRetentionMonths ?? 24,
|
||||||
|
documentType?.InternalRetentionMonths ?? 84,
|
||||||
|
entityName,
|
||||||
|
accountOwners,
|
||||||
|
documentAccess,
|
||||||
|
personNumbers,
|
||||||
|
organizationNumbers,
|
||||||
|
document.AccountNumber,
|
||||||
|
documentType?.IsDisabledInMemberView(document) ?? true,
|
||||||
|
document.ScanStatus,
|
||||||
|
() => Download(document)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new BusinessViewDocumentInformation
|
||||||
|
(
|
||||||
|
info,
|
||||||
|
(await logs).TryGetValue(document.DocumentId, out var documentLogs)
|
||||||
|
? documentLogs
|
||||||
|
: Enumerable.Empty<DocumentAuditResponse>()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
IEnumerable<BusinessViewDocumentInformation> completedDocuments = await Task.WhenAll(awaitingDocuments);
|
||||||
|
|
||||||
|
if (VisibleItemsCount == 0)
|
||||||
|
{
|
||||||
|
_toastService.ShowWarning("No documents were found with the applied filters");
|
||||||
|
}
|
||||||
|
|
||||||
|
VisibleItems.Clear();
|
||||||
|
foreach (var document in completedDocuments)
|
||||||
|
{
|
||||||
|
VisibleItems.Add(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataChanged(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
DataLoadError(this, EventArgs.Empty);
|
||||||
|
Log.Error(exception, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<AccountOwnership>> DetermineOwnerByPersonOrOrganizationNumber(DocumentMetadataResponse document)
|
||||||
|
{
|
||||||
|
var accountOwnerships = new List<AccountOwnership>();
|
||||||
|
|
||||||
|
if (EnumExtensions.IsDocumentTypeOf<PrimaryOwnerDocumentTypes>(document.Type))
|
||||||
|
{
|
||||||
|
if (document.PersonNumbers?.Count() == 1 && !string.IsNullOrWhiteSpace(document.PersonNumbers.FirstOrDefault()))
|
||||||
|
{
|
||||||
|
var person = (await _entityService.GetPersonByEntityNumber(document.PersonNumbers.FirstOrDefault()))
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (person != null)
|
||||||
|
{
|
||||||
|
accountOwnerships.Add(new AccountOwnership()
|
||||||
|
{
|
||||||
|
FirstName = person.FirstName,
|
||||||
|
MiddleInitial = person.MiddleInitial,
|
||||||
|
MiddleName = person.MiddleName,
|
||||||
|
LastName = person.LastName,
|
||||||
|
Suffix = person.Suffix,
|
||||||
|
PersonNumber = person.PersonNumber,
|
||||||
|
IsPrimaryOwner = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (document.OrganizationNumbers?.Count() == 1 &&
|
||||||
|
!string.IsNullOrWhiteSpace(document.OrganizationNumbers.FirstOrDefault()))
|
||||||
|
{
|
||||||
|
var organization =
|
||||||
|
(await _entityService.GetOrganizationByEntityNumber(document.OrganizationNumbers.FirstOrDefault()))
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (organization != null)
|
||||||
|
{
|
||||||
|
accountOwnerships.Add(new AccountOwnership()
|
||||||
|
{
|
||||||
|
OrganizationName = organization.Name,
|
||||||
|
ByOrgNbr = organization.OrganizationNumber,
|
||||||
|
IsPrimaryOwner = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (EnumExtensions.IsDocumentTypeOf<AccountDocumentTypes>(document.Type))
|
||||||
|
{
|
||||||
|
var ownersByAccountNumber =
|
||||||
|
await _entityService.GetOwnersByAccountNumber(document.AccountNumber.Value, document.CreatedDate);
|
||||||
|
accountOwnerships = ownersByAccountNumber?.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountOwnerships;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, Dictionary<string, string>> GetAccountOwners(IEnumerable<AccountOwnership> accountOwnerships)
|
||||||
|
{
|
||||||
|
var distinctAccountOwners = new Dictionary<string, Dictionary<string, string>>
|
||||||
|
{
|
||||||
|
["persons"] = new Dictionary<string, string>(),
|
||||||
|
["organizations"] = new Dictionary<string, string>()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (accountOwnerships == null)
|
||||||
|
{
|
||||||
|
return distinctAccountOwners;
|
||||||
|
}
|
||||||
|
foreach (var owner in accountOwnerships)
|
||||||
|
{
|
||||||
|
if(owner.ByOrgNbr != 0)
|
||||||
|
{
|
||||||
|
var key = owner.ByOrgNbr.ToString();
|
||||||
|
var value = GetName(owner);
|
||||||
|
if (!distinctAccountOwners["organizations"].ContainsKey(key))
|
||||||
|
{
|
||||||
|
distinctAccountOwners["organizations"].Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var key = owner.PersonNumber.ToString();
|
||||||
|
var value = GetName(owner);
|
||||||
|
if (!distinctAccountOwners["persons"].ContainsKey(key))
|
||||||
|
{
|
||||||
|
distinctAccountOwners["persons"].Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distinctAccountOwners;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AccountOwnership? GetPrimaryOwner(IEnumerable<AccountOwnership> accountOwnerships)
|
||||||
|
{
|
||||||
|
return accountOwnerships.FirstOrDefault(x => x.IsPrimaryOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPrimaryOwnerName(AccountOwnership? primaryAccountOwnership)
|
||||||
|
{
|
||||||
|
if(primaryAccountOwnership != null)
|
||||||
|
{
|
||||||
|
return GetName(primaryAccountOwnership);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "No primary owner found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DocumentAccess> GetOwnerAccess(string[] personNumbers, string[] organizationNumbers, Dictionary<string, Dictionary<string, string>> accounts, string entityName)
|
||||||
|
{
|
||||||
|
var access = new List<DocumentAccess>();
|
||||||
|
|
||||||
|
if(!accounts.Any())
|
||||||
|
{
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (personNumbers != null)
|
||||||
|
{
|
||||||
|
foreach (var account in accounts["persons"].Where(x => personNumbers.Contains(x.Key)))
|
||||||
|
{
|
||||||
|
if(!account.Value.Equals(entityName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Owner, true, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organizationNumbers != null)
|
||||||
|
{
|
||||||
|
foreach (var account in accounts["organizations"].Where(x => organizationNumbers.Contains(x.Key)))
|
||||||
|
{
|
||||||
|
if (!account.Value.Equals(entityName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Owner, true, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<DocumentAccess>> GetAdditionalAccess(string[] personNumbers, string[] organizationNumbers, Dictionary<string, Dictionary<string, string>> accounts)
|
||||||
|
{
|
||||||
|
var access = new List<DocumentAccess>();
|
||||||
|
|
||||||
|
foreach (var memberNumber in personNumbers.Where(x => !string.IsNullOrWhiteSpace(x) && !accounts["persons"].ContainsKey(x)))
|
||||||
|
{
|
||||||
|
var personEntity = (await _entityService.GetPersonByEntityNumber(memberNumber)).FirstOrDefault();
|
||||||
|
if (personEntity != null)
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Additional, true, personEntity.NameFull, personEntity.PersonNumber.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var memberNumber in organizationNumbers.Where(x => !string.IsNullOrWhiteSpace(x) && !accounts["organizations"].ContainsKey(x)))
|
||||||
|
{
|
||||||
|
var orgEntity = (await _entityService.GetOrganizationByEntityNumber(memberNumber)).FirstOrDefault();
|
||||||
|
if (orgEntity != null)
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Additional, true, orgEntity.Name, orgEntity.OrganizationNumber.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DocumentAccess> GetRemovedAccess(string[] personNumbers, string[] organizationNumbers, Dictionary<string, Dictionary<string, string>> accounts, string entityName)
|
||||||
|
{
|
||||||
|
var access = new List<DocumentAccess>();
|
||||||
|
|
||||||
|
if (!accounts["persons"].Any() && !accounts["organizations"].Any())
|
||||||
|
{
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (personNumbers == null)
|
||||||
|
{
|
||||||
|
personNumbers = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organizationNumbers == null)
|
||||||
|
{
|
||||||
|
organizationNumbers = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var account in accounts["persons"].Where(x => !personNumbers.Contains(x.Key)))
|
||||||
|
{
|
||||||
|
if (!account.Value.Equals(entityName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Removed, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var account in accounts["organizations"].Where(x => !organizationNumbers.Contains(x.Key)))
|
||||||
|
{
|
||||||
|
if (!account.Value.Equals(entityName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
access.Add(new DocumentAccess(AccessType.Removed, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool DoesPrimaryOwnerHaveAccess(AccountOwnership? primaryAccountOwnership, ICollection<string> personNumbers, ICollection<string> organizationNumbers)
|
||||||
|
{
|
||||||
|
var primaryOwnerHasAccess = false;
|
||||||
|
|
||||||
|
if (primaryAccountOwnership != null)
|
||||||
|
{
|
||||||
|
if (personNumbers != null && personNumbers.Contains(primaryAccountOwnership.PersonNumber.ToString()))
|
||||||
|
{
|
||||||
|
primaryOwnerHasAccess = true;
|
||||||
|
}
|
||||||
|
if (organizationNumbers != null && organizationNumbers.Contains(primaryAccountOwnership.ByOrgNbr.ToString()))
|
||||||
|
{
|
||||||
|
primaryOwnerHasAccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No primary owner should return true so the value does not have a strike-through on the view
|
||||||
|
primaryOwnerHasAccess = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return primaryOwnerHasAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(AccountOwnership account)
|
||||||
|
{
|
||||||
|
if(account.ByOrgNbr == 0)
|
||||||
|
{
|
||||||
|
var name = account.FirstName + " ";
|
||||||
|
if(!String.IsNullOrEmpty(account.MiddleName))
|
||||||
|
{
|
||||||
|
name += account.MiddleName + " ";
|
||||||
|
}
|
||||||
|
else if(!String.IsNullOrEmpty(account.MiddleInitial))
|
||||||
|
{
|
||||||
|
name += account.MiddleInitial + ". ";
|
||||||
|
}
|
||||||
|
name += account.LastName;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.OrganizationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnItemsPerPageValueChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.Take = ItemsPerPage = int.Parse(e.Value!.ToString()!);
|
||||||
|
Filter.DocumentProcessingFilter.Skip = 0;
|
||||||
|
|
||||||
|
await Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Refresh()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TotalItems = (int)await _documentService.CountDocuments(Filter.DocumentProcessingFilter.Filter);
|
||||||
|
Loaded = true;
|
||||||
|
|
||||||
|
await GoToPage(1);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ApplyFilterAndSorting()
|
||||||
|
{
|
||||||
|
var loading = LoadingLighthouse.Show(_modal);
|
||||||
|
|
||||||
|
Filter.DocumentProcessingFilter.Skip = 0;
|
||||||
|
Filter.DocumentProcessingFilter.Filter.PersonNumbersContainsAny = null;
|
||||||
|
Filter.DocumentProcessingFilter.Filter.OrganizationNumbersContainsAny = null;
|
||||||
|
Filter.DocumentProcessingFilter.Filter.AccountNumberContainsAny = null;
|
||||||
|
var shouldRefresh = true;
|
||||||
|
|
||||||
|
if (Filter.OwnershipType != null &&
|
||||||
|
(!string.IsNullOrWhiteSpace(Filter.OwnershipValue) || !string.IsNullOrWhiteSpace(Filter.AdditionalOwnershipValue)))
|
||||||
|
{
|
||||||
|
switch (Filter.OwnershipType)
|
||||||
|
{
|
||||||
|
case OwnershipTypes.MemberNumber:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var member = await _coreApiWrapperService.GetPersonOrOrganizationNumberByMemberId((long)Convert.ToDouble(Filter.OwnershipValue));
|
||||||
|
|
||||||
|
if (member is { PersonNumber: { } personNumber })
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.Filter.PersonNumbersContainsAny = new[] { personNumber };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member is { OrganizationNumber: { } organizationNumber })
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.Filter.OrganizationNumbersContainsAny = new[] { organizationNumber };
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
shouldRefresh = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OwnershipTypes.AccountNumber:
|
||||||
|
Filter.DocumentProcessingFilter.Filter.AccountNumberContainsAny = new [] { Filter.OwnershipValue };
|
||||||
|
break;
|
||||||
|
case OwnershipTypes.PersonName:
|
||||||
|
var request = new PersonNameRequest() { FirstName = Filter.OwnershipValue ?? "", LastName = Filter.AdditionalOwnershipValue ?? "", MdlName = "" };
|
||||||
|
const int personLimit = 50;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var personCount = await _coreApiWrapperService.GetCountPersonsByNameLike(request);
|
||||||
|
|
||||||
|
if (personCount > personLimit)
|
||||||
|
{
|
||||||
|
_toastService.ShowWarning(
|
||||||
|
$"There would be {personCount} persons in your search.\nResults have been limited to {personLimit}.\nIf you are able, please search with a more specific name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var persons = await _coreApiWrapperService.GetPersonsByNameLike(request, personLimit);
|
||||||
|
|
||||||
|
if (!persons.Any())
|
||||||
|
{
|
||||||
|
_toastService.ShowWarning("No person(s) were found.");
|
||||||
|
shouldRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter.DocumentProcessingFilter.Filter.PersonNumbersContainsAny =
|
||||||
|
persons.Select(x => x.PersonNumber.ToString()).ToArray();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
shouldRefresh = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OwnershipTypes.OrganizationName:
|
||||||
|
//50 is near the query string limit for filtering
|
||||||
|
//to raise the limit chunk the request and concatenate the results like getting document audit logs
|
||||||
|
const int organizationLimit = 50;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var checkLimit = await _coreApiWrapperService.CountOrganizationNumbersByName(Filter.OwnershipValue)
|
||||||
|
.ContinueWith(async count =>
|
||||||
|
{
|
||||||
|
if (count.Result > organizationLimit)
|
||||||
|
{
|
||||||
|
_toastService.ShowWarning(
|
||||||
|
$"There would be {await count} organizations in your search.\nResults have been limited to {organizationLimit}.\nIf you are able, please search with a more specific name.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var organizations = await _coreApiWrapperService.GetOrganizationByOrganizationName(Filter.OwnershipValue, organizationLimit);
|
||||||
|
|
||||||
|
await checkLimit;
|
||||||
|
if (!organizations.Any())
|
||||||
|
{
|
||||||
|
_toastService.ShowWarning("No organization(s) were found.");
|
||||||
|
shouldRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter.DocumentProcessingFilter.Filter.OrganizationNumbersContainsAny =
|
||||||
|
organizations.Select(x => x.OrganizationNumber.ToString()).ToArray();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
shouldRefresh = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OwnershipTypes.OrganizationNumber:
|
||||||
|
Filter.DocumentProcessingFilter.Filter.OrganizationNumbersContainsAny = new[] { Filter.OwnershipValue };
|
||||||
|
break;
|
||||||
|
case OwnershipTypes.PersonNumber:
|
||||||
|
Filter.DocumentProcessingFilter.Filter.PersonNumbersContainsAny = new[] { Filter.OwnershipValue };
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.Close();
|
||||||
|
|
||||||
|
if(shouldRefresh) await Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ClearFilterAndSorting()
|
||||||
|
{
|
||||||
|
Filter.OwnershipType = null;
|
||||||
|
Filter.OwnershipValue = null;
|
||||||
|
Filter.DocumentProcessingFilter = new DocumentInformationFilteredSearch
|
||||||
|
{
|
||||||
|
Filter = new DocumentInformationFilter(),
|
||||||
|
SortDefinition = new List<SortableField>()
|
||||||
|
};
|
||||||
|
|
||||||
|
await Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SortFieldsSelected(IList<string> fields)
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.SortDefinition = fields
|
||||||
|
.Select((fieldName, position) =>
|
||||||
|
{
|
||||||
|
var field = Filter.DocumentProcessingFilter.SortDefinition!
|
||||||
|
.FirstOrDefault(f => f.Field == fieldName)
|
||||||
|
?? new SortableField { Field = fieldName };
|
||||||
|
field.Position = position;
|
||||||
|
|
||||||
|
return field;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
await ApplyFilterAndSorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SortFieldRemoved(string field)
|
||||||
|
{
|
||||||
|
foreach (var sortField in Filter.DocumentProcessingFilter.SortDefinition!.Where(f => f.Field == field))
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.SortDefinition!.Remove(sortField);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ApplyFilterAndSorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetFieldDescending(string field) => SortField(field, true);
|
||||||
|
public Task SetFieldAscending(string field) => SortField(field, false);
|
||||||
|
private async Task SortField(string field, bool descending)
|
||||||
|
{
|
||||||
|
Filter.DocumentProcessingFilter.SortDefinition!.Single(f => f.Field == field).IsDescending = descending;
|
||||||
|
|
||||||
|
await ApplyFilterAndSorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFieldDescending(string field) => Filter.DocumentProcessingFilter.SortDefinition!.Single(f => f.Field == field).IsDescending;
|
||||||
|
|
||||||
|
private async Task Download(DocumentMetadataResponse document)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (document.DocumentBlobId is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = (await _documentService.StreamDocumentContent(document.DocumentBlobId ?? Guid.Empty)).Replace("\"", string.Empty);
|
||||||
|
var byteContent = Convert.FromBase64String(content);
|
||||||
|
using var contentStream = new DotNetStreamReference
|
||||||
|
(
|
||||||
|
new MemoryStream(byteContent)
|
||||||
|
);
|
||||||
|
|
||||||
|
await _documentService.ExternalActionPerformed(document.DocumentId, DocumentAction.Download, "Business View");
|
||||||
|
await _jsDocument.DownloadDocument($"{FileReader.GetExtensionByBytes(byteContent, document.Name)}", contentStream);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAuthorizationLists(DocumentInformation document, EntityTypes selectedEntityType, string dnaEntityNameResult, string dnaEntityIdResult)
|
||||||
|
{
|
||||||
|
var removed = document.DocumentAccess.FirstOrDefault(x => x.AccessType == AccessType.Removed && x.EntityName.Equals(dnaEntityNameResult));
|
||||||
|
if (removed != null)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Remove(removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.EntityName.Equals(dnaEntityNameResult))
|
||||||
|
{
|
||||||
|
var primaryOwner = document.DocumentAccess.Where(x => x.EntityName.Equals(dnaEntityNameResult)).FirstOrDefault();
|
||||||
|
if (primaryOwner != null)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Remove(primaryOwner);
|
||||||
|
}
|
||||||
|
document.DocumentAccess.Add(new DocumentAccess(AccessType.PrimaryOwner, true, dnaEntityNameResult, dnaEntityIdResult));
|
||||||
|
}
|
||||||
|
else if (selectedEntityType == EntityTypes.Person && document.AccountOwners["persons"].ContainsKey(dnaEntityIdResult) ||
|
||||||
|
selectedEntityType == EntityTypes.Organization && document.AccountOwners["organizations"].ContainsKey(dnaEntityIdResult))
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Add(new DocumentAccess(AccessType.Owner, true, dnaEntityNameResult, dnaEntityIdResult));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Add(new DocumentAccess(AccessType.Additional, true, dnaEntityNameResult, dnaEntityIdResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareForPrimaryOwnerDocumentRequest(DocumentInformation document, EntityTypes selectedEntityType, string dnaEntityNameResult, string dnaEntityIdResult)
|
||||||
|
{
|
||||||
|
List<DocumentAccess> accesses = new List<DocumentAccess>();
|
||||||
|
|
||||||
|
foreach (var account in document.AccountOwners["persons"])
|
||||||
|
{
|
||||||
|
if (document.EntityName.Equals(account.Value))
|
||||||
|
{
|
||||||
|
accesses.Add(new DocumentAccess(AccessType.PrimaryOwner, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
accesses.Add(new DocumentAccess(AccessType.Removed, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var account in document.AccountOwners["organizations"])
|
||||||
|
{
|
||||||
|
if (document.EntityName.Equals(account.Value))
|
||||||
|
{
|
||||||
|
accesses.Add(new DocumentAccess(AccessType.PrimaryOwner, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
accesses.Add(new DocumentAccess(AccessType.Removed, false, account.Value, account.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.DocumentAccess.Clear();
|
||||||
|
document.PersonNumbers.Clear();
|
||||||
|
document.OrganizationNumbers.Clear();
|
||||||
|
|
||||||
|
if (selectedEntityType == EntityTypes.Person)
|
||||||
|
{
|
||||||
|
document.PersonNumbers.Add(dnaEntityIdResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.OrganizationNumbers.Add(dnaEntityIdResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var access in accesses)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Add(access);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DocumentMetadataUpdate(DocumentInformation document, EntityTypes selectedEntityType, string dnaEntityIdResult)
|
||||||
|
=> await _documentTypeUploaderService.DocumentMetadataUpdate(document, selectedEntityType, dnaEntityIdResult);
|
||||||
|
|
||||||
|
public void RemoveAuthorizationFromDocument(DocumentInformation document, string dnaEntityNameResult, string dnaEntityIdResult)
|
||||||
|
{
|
||||||
|
var primaryowner = document.DocumentAccess.Where(x => x.AccessType == AccessType.PrimaryOwner && x.EntityName.Equals(dnaEntityNameResult)).FirstOrDefault();
|
||||||
|
if (primaryowner != null)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Remove(primaryowner);
|
||||||
|
document.DocumentAccess.Add(new DocumentAccess(AccessType.PrimaryOwner, false, dnaEntityNameResult, dnaEntityIdResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
var owner = document.DocumentAccess.Where(x => x.AccessType == AccessType.Owner && x.EntityName.Equals(dnaEntityNameResult)).FirstOrDefault();
|
||||||
|
if (owner != null)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Remove(owner);
|
||||||
|
document.DocumentAccess.Add(new DocumentAccess(AccessType.Removed, false, dnaEntityNameResult, dnaEntityIdResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
var additional = document.DocumentAccess.Where(x => x.AccessType == AccessType.Additional && x.EntityName.Equals(dnaEntityNameResult)).FirstOrDefault();
|
||||||
|
if (additional != null)
|
||||||
|
{
|
||||||
|
document.DocumentAccess.Remove(additional);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Person?> GetPersonByPersonNumber(string DnaEntityIdSearch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (await _entityService.GetPersonByEntityNumber(DnaEntityIdSearch)).FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Organization?> GetOrganizationByOrganizationNumber(string DnaEntityIdSearch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (await _entityService.GetOrganizationByEntityNumber(DnaEntityIdSearch)).FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (DateTime CreationDateStart, DateTime CreationDateEnd) DefaultFilterDateRange(DefaultFilters configuration)
|
||||||
|
=> (
|
||||||
|
DateTime.Now.Date.AddDays(configuration.CreationDateStartOffsetDays),
|
||||||
|
DateTime.Now.Date.AddDays(configuration.CreationDateEndOffsetDays).AddDays(1).AddTicks(-1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileReader
|
||||||
|
{
|
||||||
|
private static readonly byte[] Bmp = { 66, 77 };
|
||||||
|
private static readonly byte[] Doc = { 208, 207, 17, 224, 161, 177, 26, 225 };
|
||||||
|
private static readonly byte[] ExeDll = { 77, 90 };
|
||||||
|
private static readonly byte[] Gif = { 71, 73, 70, 56 };
|
||||||
|
private static readonly byte[] Ico = { 0, 0, 1, 0 };
|
||||||
|
private static readonly byte[] Jpg = { 255, 216, 255 };
|
||||||
|
private static readonly byte[] Mp3 = { 255, 251, 48 };
|
||||||
|
private static readonly byte[] Pdf = { 37, 80, 68, 70, 45, 49, 46 };
|
||||||
|
private static readonly byte[] Png = { 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82 };
|
||||||
|
private static readonly byte[] Rar = { 82, 97, 114, 33, 26, 7, 0 };
|
||||||
|
private static readonly byte[] Swf = { 70, 87, 83 };
|
||||||
|
private static readonly byte[] Tiff = { 73, 73, 42, 0 };
|
||||||
|
private static readonly byte[] Ttf = { 0, 1, 0, 0, 0 };
|
||||||
|
private static readonly byte[] Avi = { 82, 73, 70, 70 };
|
||||||
|
private static readonly byte[] Wma = { 48, 38, 178, 117, 142, 102, 207, 17, 166, 217, 0, 170, 0, 98, 206, 108 };
|
||||||
|
private static readonly byte[] DocX = { 80, 75, 3, 4 };
|
||||||
|
|
||||||
|
public static string GetExtensionByBytes(byte[] file, string fileName)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(Path.GetExtension(fileName))) return fileName;
|
||||||
|
|
||||||
|
if (file.Take(2).SequenceEqual(Bmp)) return $"{fileName}.bmp";
|
||||||
|
if (file.Take(2).SequenceEqual(ExeDll)) return $"{fileName}.exe";
|
||||||
|
if (file.Take(3).SequenceEqual(Jpg)) return $"{fileName}.jpg";
|
||||||
|
if (file.Take(3).SequenceEqual(Mp3)) return $"{fileName}.mp3";
|
||||||
|
if (file.Take(3).SequenceEqual(Swf)) return $"{fileName}.swf";
|
||||||
|
if (file.Take(4).SequenceEqual(Avi)) return $"{fileName}.avi";
|
||||||
|
if (file.Take(4).SequenceEqual(DocX)) return $"{fileName}.docx";
|
||||||
|
if (file.Take(4).SequenceEqual(Gif)) return $"{fileName}.gif";
|
||||||
|
if (file.Take(4).SequenceEqual(Ico)) return $"{fileName}.ico";
|
||||||
|
if (file.Take(4).SequenceEqual(Tiff)) return $"{fileName}.tiff";
|
||||||
|
if (file.Take(5).SequenceEqual(Ttf)) return $"{fileName}.ttf";
|
||||||
|
if (file.Take(7).SequenceEqual(Pdf)) return $"{fileName}.pdf";
|
||||||
|
if (file.Take(7).SequenceEqual(Rar)) return $"{fileName}.rar";
|
||||||
|
if (file.Take(8).SequenceEqual(Doc)) return $"{fileName}.doc";
|
||||||
|
if (file.Take(16).SequenceEqual(Png)) return $"{fileName}.png";
|
||||||
|
if (file.Take(16).SequenceEqual(Wma)) return $"{fileName}.wma";
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
+211
@@ -0,0 +1,211 @@
|
|||||||
|
@using LCU.Library.Internal.DocumentIngestorModels.Models
|
||||||
|
@inject AuthenticationHelper AuthorizationHelper;
|
||||||
|
@inject LCU.Library.Internal.Security.Service.IAzureAdAuthorizationService AzureAdAuthorizationService
|
||||||
|
@inject IModalService Modal;
|
||||||
|
@inject IDocumentProcessingInfoService DocumentProcessingInfoService;
|
||||||
|
@inject IToastService ToastService;
|
||||||
|
|
||||||
|
@if (processStatusTypes.Any()) {
|
||||||
|
<CollapsibleComponent ButtonClass="btn btn-link"
|
||||||
|
HeaderClass="row grid-header-row grid-subheader-row-container processing-header"
|
||||||
|
ButtonContainerClass="grid-button-container"
|
||||||
|
IsCollapsed="false">
|
||||||
|
<HeaderContent>
|
||||||
|
<RowSubHeader>Update Process Status</RowSubHeader>
|
||||||
|
</HeaderContent>
|
||||||
|
<BodyContent>
|
||||||
|
<Confirm @ref="UpdateStatusConfirmation" ConfirmationChanged="UpdateStatus_Confirm"
|
||||||
|
ConfirmationMessage=@("Are you sure you want to update the processing status" +
|
||||||
|
$" from {DocumentProcessingInfo.StatusType} to {SelectedStatus}?")
|
||||||
|
ConfirmationTitle=@("Update Status")>
|
||||||
|
</Confirm>
|
||||||
|
<div class="d-flex grid-row-container-bordered">
|
||||||
|
<div class="col-md-3 col-with-right-boarder">
|
||||||
|
<div class="form-group form-row">
|
||||||
|
<label for="StatusTypes" class="col-sm-12 col-form-label">Change Status to:</label>
|
||||||
|
<select class="form-control" id="StatusTypes" @bind="SelectedStatus" @oninput="ChangeStatusType">
|
||||||
|
<option></option>
|
||||||
|
@foreach (var status in processStatusTypes)
|
||||||
|
{
|
||||||
|
<option value="@status">@status</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-with-right-boarder">
|
||||||
|
<div class="form-group form-row">
|
||||||
|
<label for="ReasonForChange" class="col-sm-12 col-form-label">Reason for Change:</label>
|
||||||
|
<input class="form-control" id="ReasonForChange" placeholder="" @oninput="ChangeReasonMessage" @bind="UpdateStatusRequest.ReasonMessage"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div>
|
||||||
|
<label for="UpdateStatus" class="col-sm-12 col-form-label"> </label>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
@onclick="UpdateStatus_Click"
|
||||||
|
title="Update Status"
|
||||||
|
id="UpdateStatus"
|
||||||
|
disabled="@Disabled">
|
||||||
|
Update Process Status
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BodyContent>
|
||||||
|
</CollapsibleComponent>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfo DocumentProcessingInfo { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<IModalReference> RefreshData { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public DocumentProcessingInfoStatusRequest UpdateStatusRequest { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<ProcessStatusTypes> processStatusTypes { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Disabled { get; set; } = true;
|
||||||
|
|
||||||
|
private ConfirmBase? UpdateStatusConfirmation { get; set; }
|
||||||
|
|
||||||
|
private ProcessStatusTypes? SelectedStatus { get; set; } = null;
|
||||||
|
|
||||||
|
private readonly Dictionary<ProcessStatusTypes, List<ProcessStatusTypes>> _statusChangeRejectionList =
|
||||||
|
new ()
|
||||||
|
{
|
||||||
|
[ProcessStatusTypes.Processing] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Reprocessing,
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.Completed] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Processing,
|
||||||
|
ProcessStatusTypes.Completed,
|
||||||
|
ProcessStatusTypes.ArchiveFailure,
|
||||||
|
ProcessStatusTypes.CriticalFailure,
|
||||||
|
ProcessStatusTypes.DataMappingFailure,
|
||||||
|
ProcessStatusTypes.Reprocessing,
|
||||||
|
ProcessStatusTypes.NotificationDataFailure,
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.ArchiveFailure] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.CriticalFailure] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.DataMappingFailure] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Processing,
|
||||||
|
ProcessStatusTypes.Completed,
|
||||||
|
ProcessStatusTypes.ArchiveFailure,
|
||||||
|
ProcessStatusTypes.CriticalFailure,
|
||||||
|
ProcessStatusTypes.DataMappingFailure,
|
||||||
|
ProcessStatusTypes.Reprocessing,
|
||||||
|
ProcessStatusTypes.NotificationDataFailure,
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.Reprocessing] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Processing,
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.NotificationDataFailure] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Processing,
|
||||||
|
ProcessStatusTypes.ArchiveFailure,
|
||||||
|
ProcessStatusTypes.CriticalFailure,
|
||||||
|
ProcessStatusTypes.DataMappingFailure,
|
||||||
|
ProcessStatusTypes.Reprocessing,
|
||||||
|
ProcessStatusTypes.UploadCompletionReprocessing
|
||||||
|
},
|
||||||
|
[ProcessStatusTypes.UploadCompletionReprocessing] = new()
|
||||||
|
{
|
||||||
|
ProcessStatusTypes.Processing,
|
||||||
|
ProcessStatusTypes.ArchiveFailure,
|
||||||
|
ProcessStatusTypes.CriticalFailure,
|
||||||
|
ProcessStatusTypes.DataMappingFailure,
|
||||||
|
ProcessStatusTypes.Reprocessing
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
processStatusTypes = GetStatusTypes();
|
||||||
|
UpdateStatusRequest = new DocumentProcessingInfoStatusRequest()
|
||||||
|
{
|
||||||
|
DocumentProcessingInfoId = DocumentProcessingInfo.DocumentProcessingInfoId,
|
||||||
|
ProcessStatus = DocumentProcessingInfo.StatusType,
|
||||||
|
Username = AzureAdAuthorizationService.GetUserName()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ProcessStatusTypes> GetStatusTypes()
|
||||||
|
{
|
||||||
|
return Enum.GetValues<ProcessStatusTypes>()
|
||||||
|
.Where(x => !_statusChangeRejectionList[DocumentProcessingInfo.StatusType].Contains(x)
|
||||||
|
&& x != DocumentProcessingInfo.StatusType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeStatusType(ChangeEventArgs changeEventArgs)
|
||||||
|
{
|
||||||
|
var value = changeEventArgs.Value?.ToString();
|
||||||
|
if(!string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
SelectedStatus = UpdateStatusRequest.ProcessStatus = Enum.Parse<ProcessStatusTypes>(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectedStatus = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeReasonMessage(ChangeEventArgs changeEventArgs)
|
||||||
|
{
|
||||||
|
UpdateStatusRequest.ReasonMessage = changeEventArgs.Value?.ToString();
|
||||||
|
|
||||||
|
ValidateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateForm()
|
||||||
|
{
|
||||||
|
Disabled = string.IsNullOrWhiteSpace(UpdateStatusRequest.ReasonMessage) ||
|
||||||
|
string.IsNullOrWhiteSpace(UpdateStatusRequest.Username) ||
|
||||||
|
SelectedStatus is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus_Click()
|
||||||
|
{
|
||||||
|
UpdateStatusConfirmation?.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateStatus_Confirm(bool confirmed)
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
var openModal = LoadingLighthouse.Show(Modal);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await DocumentProcessingInfoService.UpdateStatus(UpdateStatusRequest);
|
||||||
|
ToastService.ShowInfo("Document status has been updated.");
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
|
||||||
|
await RefreshData.InvokeAsync(openModal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
@using DocumentTypeRetriever
|
||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@inject IModalService Modal;
|
||||||
|
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.DocCodeDetailsCreate">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
title="New"
|
||||||
|
@onclick="@ShowNewDocCodeDetailsModal"
|
||||||
|
style="margin-left: -0.5rem;">
|
||||||
|
New
|
||||||
|
</button>
|
||||||
|
</AuthorizeView>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public CreateDocumentCodeDetails CreateDocumentCodeDetails { get; set; }
|
||||||
|
|
||||||
|
private async Task ShowNewDocCodeDetailsModal()
|
||||||
|
{
|
||||||
|
var modal = await DocCodeDetailsCreationModal.Show(Modal).Result;
|
||||||
|
if (modal.Confirmed && modal.Data is DocumentCodeDetailsRequest newDetails)
|
||||||
|
{
|
||||||
|
await CreateDocumentCodeDetails(newDetails);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@using DocumentTypeRetriever
|
||||||
|
@using DocumentTypes = DocumentTypeRetriever.DocumentTypes
|
||||||
|
|
||||||
|
@inject IJSTooltips JsTooltips;
|
||||||
|
|
||||||
|
<EditForm Model="@_newDetails" OnValidSubmit="@ReturnNewDetails">
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="document-code" class="required text-nowrap">Document Code:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<InputText id="document-code"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@oninput="@(async change => {_newDetails.DocumentCode = change.Value!.ToString(); await Validate(change);})"
|
||||||
|
@bind-Value="@_newDetails.DocumentCode"/>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DocumentCode)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="document-business-name" class="required text-no-wrap">Document Business Name:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<InputText id="document-business-name"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@oninput="@(async change => {_newDetails.DocumentBusinessName = change.Value!.ToString(); await Validate(change);})"
|
||||||
|
@bind-Value="@_newDetails.DocumentBusinessName"/>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DocumentBusinessName)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="document-member-facing-name" class="required text-no-wrap">Document Member Facing Name:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<InputText id="document-member-facing-name"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@oninput="@(async change => {_newDetails.DocumentMemberFacingName = change.Value!.ToString(); await Validate(change);})"
|
||||||
|
@bind-Value="@_newDetails.DocumentMemberFacingName"/>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DocumentMemberFacingName)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="document-type" class="required text-no-wrap">Document Type:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<InputSelect id="document-type"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@bind-Value="@_newDetails.DocumentType">
|
||||||
|
@foreach (var option in Enum.GetValues<DocumentTypes>())
|
||||||
|
{
|
||||||
|
<option value="@option">@option</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DocumentType)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="document-disable-member-view" class="text-nowrap">
|
||||||
|
Disable Member View <i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="If true documents of this type will be disabled in member view regardless of start or end date."></i>:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 justify-content-center">
|
||||||
|
<InputCheckbox id="document-disable-member-view" @bind-Value="@_newDetails.DisableMemberView"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="document-disable-member-view-start-date" class="text-nowrap">
|
||||||
|
Disable Member View Start <i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="This type of document will be disabled in member view from this date to the disable end date only if there is also an end date."></i>:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<InputDate id="document-disable-member-view-start-date"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@oninput="@(async change => {
|
||||||
|
_newDetails.DisableMemberViewStartDate = Convert.ToDateTime(change.Value);
|
||||||
|
await Validate(change);
|
||||||
|
})"
|
||||||
|
@bind-Value="@_newDetails.DisableMemberViewStartDate"/>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DisableMemberViewStartDate)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="document-disable-member-view-end-date" class="text-nowrap">
|
||||||
|
Disable Member View End <i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="This type of document will be disabled in member from the start date until this date if there is a start date. No value will result in 'start date - Forever'."></i>:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<InputDate id="document-disable-member-view-end-date"
|
||||||
|
class="form-control flex-fill"
|
||||||
|
@oninput="@(async change => {
|
||||||
|
_newDetails.DisableMemberViewEndDate = Convert.ToDateTime(change.Value);
|
||||||
|
await Validate(change);
|
||||||
|
})"
|
||||||
|
@bind-Value="@_newDetails.DisableMemberViewEndDate"/>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.DisableMemberViewEndDate)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="online-banking-retention-months" class="required text-no-wrap">Online Retention:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<InputNumber id="online-banking-retention-months"
|
||||||
|
TValue="int?"
|
||||||
|
class="form-control flex-fill col-5"
|
||||||
|
style="display: inline-block"
|
||||||
|
@oninput="@(async change => {
|
||||||
|
_newDetails.OnlineBankingRetentionMonths = int.TryParse(change.Value?.ToString(), out var x) ? x : null;
|
||||||
|
await Validate(change);
|
||||||
|
})"
|
||||||
|
@bind-Value="@_newDetails.OnlineBankingRetentionMonths"/>
|
||||||
|
<span class="mt-2 col">months</span>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.OnlineBankingRetentionMonths)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row py-2">
|
||||||
|
<div class="col">
|
||||||
|
<label for="internal-retention-months" class="required text-no-wrap">Internal Retention:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<InputNumber id="internal-retention-months"
|
||||||
|
TValue="int?"
|
||||||
|
class="form-control flex-fill col-5"
|
||||||
|
style="display: inline-block"
|
||||||
|
@oninput="@(async change => {
|
||||||
|
_newDetails.InternalRetentionMonths = int.TryParse(change.Value?.ToString(), out var x) ? x : null;
|
||||||
|
await Validate(change);
|
||||||
|
})"
|
||||||
|
@bind-Value="@_newDetails.InternalRetentionMonths"/>
|
||||||
|
<span class="mt-2 col">months</span>
|
||||||
|
</div>
|
||||||
|
<span class="col-12">
|
||||||
|
<ValidationMessage For="@(() => _newDetails.InternalRetentionMonths)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row pt-2 flex-row-reverse">
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary mr-1" disabled="@_saveIsDisabled">Submit</button>
|
||||||
|
<button type="button" class="btn btn-secondary py-2" @onclick="@Cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FluentValidationValidator @ref="_fluentValidationValidator" Options="@(options => options.IncludeRuleSets(_ruleSets))"/>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
public BlazoredModalInstance BlazoredModal { get; set; }
|
||||||
|
|
||||||
|
public static IModalReference Show(IModalService modal)
|
||||||
|
{
|
||||||
|
var options = new ModalOptions
|
||||||
|
{
|
||||||
|
Position = ModalPosition.Middle
|
||||||
|
};
|
||||||
|
|
||||||
|
return modal.Show<DocCodeDetailsCreationModal>("New Doc Code Details", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly DocumentCodeDetails _newDetails = new();
|
||||||
|
private FluentValidationValidator? _fluentValidationValidator;
|
||||||
|
private readonly string[] _ruleSets = {"DocumentCodeDetails"};
|
||||||
|
private bool _saveIsDisabled = true;
|
||||||
|
|
||||||
|
public class DocumentCodeDetails
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string DocumentCode { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string DocumentBusinessName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string DocumentMemberFacingName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DocumentTypes DocumentType { get; set; }
|
||||||
|
|
||||||
|
public bool DisableMemberView { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DisableMemberViewStartDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DisableMemberViewEndDate { get; set; }
|
||||||
|
|
||||||
|
public int? OnlineBankingRetentionMonths { get; set; } = 24;
|
||||||
|
|
||||||
|
public int? InternalRetentionMonths { get; set; } = 84;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) await JsTooltips.Enable();
|
||||||
|
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReturnNewDetails()
|
||||||
|
{
|
||||||
|
var newDetails = new DocumentCodeDetailsRequest
|
||||||
|
{
|
||||||
|
DocumentCode = _newDetails.DocumentCode,
|
||||||
|
DocumentBusinessName = _newDetails.DocumentBusinessName,
|
||||||
|
DocumentMemberFacingName = _newDetails.DocumentMemberFacingName,
|
||||||
|
DocumentType = _newDetails.DocumentType,
|
||||||
|
DisableMemberView = _newDetails.DisableMemberView,
|
||||||
|
DisableMemberViewStartDate = _newDetails.DisableMemberViewStartDate,
|
||||||
|
DisableMemberViewEndDate = _newDetails.DisableMemberViewEndDate,
|
||||||
|
OnlineBankingRetentionMonths = _newDetails.OnlineBankingRetentionMonths ?? 24,
|
||||||
|
InternalRetentionMonths = _newDetails.InternalRetentionMonths ?? 84
|
||||||
|
};
|
||||||
|
|
||||||
|
await BlazoredModal.CloseAsync(ModalResult.Ok(newDetails));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Cancel()
|
||||||
|
{
|
||||||
|
await BlazoredModal.CancelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Validate(EventArgs args)
|
||||||
|
{
|
||||||
|
_saveIsDisabled = !await _fluentValidationValidator!.ValidateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
@using LCU.WebApps.Internal.DocumentManager.Models.Constants
|
||||||
|
@inject IJSTooltips JSTooltips;
|
||||||
|
@inject AuthenticationHelper Auth;
|
||||||
|
|
||||||
|
<RowSubHeader>Document Attributes</RowSubHeader>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="col">
|
||||||
|
<div class="row font-weight-bold">
|
||||||
|
Customer Facing Name:
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
@CustomerFacingName
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row font-weight-bold">
|
||||||
|
Account Number:
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
@(AccountNumber ?? "Unknown")
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row font-weight-bold">
|
||||||
|
Entity Name:
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
@EntityName
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AuthorizeView Policy="@PolicyConstants.DocumentLookupShowDocumentId">
|
||||||
|
<div class="row font-weight-bold">
|
||||||
|
Document ID:
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
@DocumentId
|
||||||
|
</div>
|
||||||
|
</AuthorizeView>
|
||||||
|
|
||||||
|
@if (!VisibleInMemberView)
|
||||||
|
{
|
||||||
|
<div class="row"> </div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1 align-self-center text-center font-weight-bold doc-type-disabled-warning">!</div>
|
||||||
|
<div class="col">This document is currently hidden from the member portal</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!String.IsNullOrEmpty(RetentionMessage))
|
||||||
|
{
|
||||||
|
<div class="row"> </div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1 align-self-center text-center font-weight-bold doc-type-disabled-warning">!</div>
|
||||||
|
<div class="col">
|
||||||
|
<p class="mb-1">@RetentionMessage</p>
|
||||||
|
<p class="mb-0">Authorizations cannot be modified.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (ScanStatus == false)
|
||||||
|
{
|
||||||
|
<div class="row"> </div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1 p-0 align-self-center text-center font-weight-bold doc-type-disabled-warning">
|
||||||
|
<i class="fa fa-warning"></i>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<p>Security scan marked this file as malicious and it may not be downloaded or updated.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!DateIsAfterInternalRetentionDate() && ScanStatus != false && IsAllowedToDownload)
|
||||||
|
{
|
||||||
|
<div class="row justify-content-center mt-4 mb-2">
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="DownloadDocument" title="Download" disabled="@(!Scanned)">
|
||||||
|
Download
|
||||||
|
@if (!Scanned)
|
||||||
|
{
|
||||||
|
<i class="fa fa-info-circle" style="cursor: pointer;" data-toggle="tooltip" data-placement="top" title="This document cannot be downloaded, it is queued for a security scan."></i>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string CustomerFacingName { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string AccountNumber { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string EntityName { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool VisibleInMemberView { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Func<Task> DownloadDocument { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int OnlineBankingRetentionMonths { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int InternalRetentionMonths { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string DocumentId { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool? ScanStatus { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Scanned { get; set; }
|
||||||
|
|
||||||
|
private bool IsAllowedToDownload => Auth.PolicySatisfied(PolicyConstants.DocumentLookupDownload);
|
||||||
|
|
||||||
|
private string RetentionMessage { get; set; } = String.Empty;
|
||||||
|
|
||||||
|
private bool DateIsAfterInternalRetentionDate() => (DateTime.Now > CreatedDate.AddMonths(InternalRetentionMonths));
|
||||||
|
private bool DateIsAfterOnlineRetentionDate() => (DateTime.Now > CreatedDate.AddMonths(OnlineBankingRetentionMonths));
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (DateIsAfterInternalRetentionDate())
|
||||||
|
{
|
||||||
|
RetentionMessage = "Document is past the internal retention period and is not available for download.";
|
||||||
|
} else if (DateIsAfterOnlineRetentionDate())
|
||||||
|
{
|
||||||
|
RetentionMessage = "Document is past the online retention period.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender) JSTooltips.Enable();
|
||||||
|
|
||||||
|
return base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
.doc-type-disabled-warning {
|
||||||
|
background: rgba(255, 0, 0, .2);
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
@using LCU.Library.DocumentType.Extensions
|
||||||
|
@using LCU.Library.DocumentType.Enums
|
||||||
|
|
||||||
|
<RowSubHeader>Document Authorizations</RowSubHeader>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="col col-4">
|
||||||
|
<strong>
|
||||||
|
Primary:
|
||||||
|
</strong>
|
||||||
|
<ul>
|
||||||
|
@if(PrimaryOwnerHasAccess)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
@PrimaryOwnerName
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if(!PrimaryOwnerHasAccess)
|
||||||
|
{
|
||||||
|
<li class="removed">
|
||||||
|
@PrimaryOwnerName
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col col-8">
|
||||||
|
<strong>
|
||||||
|
Additional:
|
||||||
|
</strong>
|
||||||
|
<ul>
|
||||||
|
@foreach(var access in Document.DocumentAccess.Where(x => x.AccessType == AccessType.Owner))
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
@access.EntityName
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@foreach(var access in Document.DocumentAccess.Where(x => x.AccessType == AccessType.Additional))
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<span class="oi oi-eye mr-2"></span>@access.EntityName
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if (EnumExtensions.IsDocumentTypeOf<AccountDocumentTypes>(Document.DocumentCode))
|
||||||
|
{
|
||||||
|
@foreach(var access in Document.DocumentAccess.Where(x => x.AccessType == AccessType.Removed))
|
||||||
|
{
|
||||||
|
<li class="removed">
|
||||||
|
@access.EntityName
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public DocumentInformation Document { get; set; }
|
||||||
|
|
||||||
|
private string PrimaryOwnerName { get; set; }
|
||||||
|
private bool PrimaryOwnerHasAccess { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
UpdatePrimaryOwner();
|
||||||
|
|
||||||
|
Document.DocumentAccess.CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
UpdatePrimaryOwner();
|
||||||
|
StateHasChanged();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePrimaryOwner()
|
||||||
|
{
|
||||||
|
var primaryOwner = Document.DocumentAccess.FirstOrDefault(x => x.AccessType == AccessType.PrimaryOwner);
|
||||||
|
if(primaryOwner != null)
|
||||||
|
{
|
||||||
|
PrimaryOwnerName = primaryOwner.EntityName;
|
||||||
|
PrimaryOwnerHasAccess = primaryOwner.HasAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removed {
|
||||||
|
text-decoration: line-through red;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user