diff --git a/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj
new file mode 100644
index 0000000000..30e3e9a12e
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj
@@ -0,0 +1,239 @@
+
+
+ Project
+ 16.0.5270.0
+ 9.0.1.0
+ $base64$PFNvdXJjZUNvbnRyb2xJbmZvIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOmRkbDI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDAzL2VuZ2luZS8yIiB4bWxuczpkZGwyXzI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDAzL2VuZ2luZS8yLzIiIHhtbG5zOmRkbDEwMF8xMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDA4L2VuZ2luZS8xMDAvMTAwIiB4bWxuczpkZGwyMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEwL2VuZ2luZS8yMDAiIHhtbG5zOmRkbDIwMF8yMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEwL2VuZ2luZS8yMDAvMjAwIiB4bWxuczpkZGwzMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDExL2VuZ2luZS8zMDAiIHhtbG5zOmRkbDMwMF8zMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDExL2VuZ2luZS8zMDAvMzAwIiB4bWxuczpkZGw0MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEyL2VuZ2luZS80MDAiIHhtbG5zOmRkbDQwMF80MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEyL2VuZ2luZS80MDAvNDAwIiB4bWxuczpkZGw1MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEzL2VuZ2luZS81MDAiIHhtbG5zOmRkbDUwMF81MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEzL2VuZ2luZS81MDAvNTAwIiB4bWxuczpkd2Q9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vRGF0YVdhcmVob3VzZS9EZXNpZ25lci8xLjAiPg0KICA8RW5hYmxlZD5mYWxzZTwvRW5hYmxlZD4NCiAgPFByb2plY3ROYW1lPjwvUHJvamVjdE5hbWU+DQogIDxBdXhQYXRoPjwvQXV4UGF0aD4NCiAgPExvY2FsUGF0aD48L0xvY2FsUGF0aD4NCiAgPFByb3ZpZGVyPjwvUHJvdmlkZXI+DQo8L1NvdXJjZUNvbnRyb2xJbmZvPg==
+
+ PIMS_NOTE_FIX.database
+ PIMS_NOTE_FIX.database
+
+
+
+
+
+
+
+ {fc40ba87-9d30-436c-9170-6e5855013b8a}
+ PIMS_NOTE_FIX
+ 0
+ 0
+ 0
+
+
+ 2024-11-13T09:03:26.4047971-08:00
+ ARIMA-DELL\smart
+ ARIMA-DELL
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ false
+ 3
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ Data Source=sqldevtst.th.gov.bc.ca;Initial Catalog=PIMS_DEV;Provider=SQLOLEDB.1;Integrated Security=SSPI;Auto Translate=False;Application Name=SSIS-PIMS_NOTE_FIX-{C09DFD26-68EF-4174-BD87-9FD55350282E}sqldevtst.th.gov.bc.ca.PIMS_DEV;
+ 18
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 1
+ 9
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 5
+ 9
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ false
+ 3
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ PIMS_DEV
+ 18
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 1
+ 18
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ false
+ 3
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ sqldevtst.th.gov.bc.ca
+ 18
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 18
+
+
+
+
+
+
+ {ED2FA4DA-FEB6-4E95-8ED5-C9E9EDAA5BE2}
+ Package1
+ 1
+ 0
+ 15
+
+
+ {4EF9E78B-4F43-43F1-A01E-D06BBF18DE3C}
+ 8
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ Development
+
+ bin
+
+
+
+
+ SQLServer2022
+ false
+
+
+
+
+
+
+
+ LastModifiedTime
+ LastModifiedTime
+ 2024-11-18T05:26:08.7662341Z
+
+
+
+
+
+
\ No newline at end of file
diff --git a/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj.user b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj.user
new file mode 100644
index 0000000000..73a600cad0
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtproj.user
@@ -0,0 +1,27 @@
+
+
+
+
+ Development
+
+
+ false
+
+
+ ssistst.th.gov.bc.ca
+ /SSISDB/PIMS_DEV/PIMS_NOTE_FIX
+
+
+ false
+ true
+
+
+ LastModifiedTime
+ LastModifiedTime
+ 2024-11-18T05:26:08.7672299Z
+
+
+
+
+
+
\ No newline at end of file
diff --git a/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtsx b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtsx
new file mode 100644
index 0000000000..9ede42efc8
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.dtsx
@@ -0,0 +1,272 @@
+
+
+ 8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
\ No newline at end of file
diff --git a/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.sln b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.sln
new file mode 100644
index 0000000000..d70d322caf
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/PIMS_NOTE_FIX.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.34729.46
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{C9674DCB-5085-4A16-B785-4C70DD1589BD}") = "PIMS_NOTE_FIX", "PIMS_NOTE_FIX.dtproj", "{3ED37DC9-D807-4CF1-91D2-DF9C3A2A5A38}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Development|Default = Development|Default
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3ED37DC9-D807-4CF1-91D2-DF9C3A2A5A38}.Development|Default.ActiveCfg = Development
+ {3ED37DC9-D807-4CF1-91D2-DF9C3A2A5A38}.Development|Default.Build.0 = Development
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AE9C9C94-8B5B-46A6-8097-910675F41D31}
+ EndGlobalSection
+EndGlobal
diff --git a/etl/PIMS_NOTE_FIX/Project.params b/etl/PIMS_NOTE_FIX/Project.params
new file mode 100644
index 0000000000..680ffe30da
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/Project.params
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/etl/PIMS_NOTE_FIX/sqldevtst.th.gov.bc.ca.PIMS_DEV.conmgr b/etl/PIMS_NOTE_FIX/sqldevtst.th.gov.bc.ca.PIMS_DEV.conmgr
new file mode 100644
index 0000000000..23c308d9a6
--- /dev/null
+++ b/etl/PIMS_NOTE_FIX/sqldevtst.th.gov.bc.ca.PIMS_DEV.conmgr
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/backend/api/Areas/CompensationRequisition/Controllers/CompensationRequisitionController.cs b/source/backend/api/Areas/CompensationRequisition/Controllers/CompensationRequisitionController.cs
index d1bcd51f94..487f5a978e 100644
--- a/source/backend/api/Areas/CompensationRequisition/Controllers/CompensationRequisitionController.cs
+++ b/source/backend/api/Areas/CompensationRequisition/Controllers/CompensationRequisitionController.cs
@@ -225,5 +225,26 @@ public IActionResult GetCompensationRequisitionFinancials([FromRoute] long id)
return new JsonResult(_mapper.Map>(compReqFinancials));
}
+
+ [HttpGet("{id:long}/payees")]
+ [HasPermission(Permissions.CompensationRequisitionView)]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(List), 200)]
+ [SwaggerOperation(Tags = new[] { "compensation-requisition" })]
+ [TypeFilter(typeof(NullJsonResultFilter))]
+ public IActionResult GetCompensationRequisitionPayees([FromRoute] long id)
+ {
+ _logger.LogInformation(
+ "Request received by Controller: {Controller}, Action: {ControllerAction}, User: {User}, DateTime: {DateTime}",
+ nameof(CompensationRequisitionController),
+ nameof(GetCompensationRequisitionPayees),
+ User.GetUsername(),
+ DateTime.Now);
+ _logger.LogInformation("Dispatching to service: {Service}", _compensationRequisitionService.GetType());
+
+ var compReqPayees = _compensationRequisitionService.GetCompensationRequisitionPayees(id);
+
+ return new JsonResult(_mapper.Map>(compReqPayees));
+ }
}
}
diff --git a/source/backend/api/Areas/Leases/Controllers/LeaseStakeholderController.cs b/source/backend/api/Areas/Leases/Controllers/LeaseStakeholderController.cs
index f462da383b..b7222976b7 100644
--- a/source/backend/api/Areas/Leases/Controllers/LeaseStakeholderController.cs
+++ b/source/backend/api/Areas/Leases/Controllers/LeaseStakeholderController.cs
@@ -69,9 +69,9 @@ public IActionResult GetStakeholders(long leaseId)
User.GetUsername(),
DateTime.Now);
- var updatedLease = _leaseService.GetStakeholdersByLeaseId(leaseId);
+ var leaseStakeholders = _leaseService.GetStakeholdersByLeaseId(leaseId);
- return new JsonResult(_mapper.Map>(updatedLease));
+ return new JsonResult(_mapper.Map>(leaseStakeholders));
}
///
diff --git a/source/backend/api/Areas/Projects/Controllers/ProjectController.cs b/source/backend/api/Areas/Projects/Controllers/ProjectController.cs
index 683ec1b4b3..9a600d73cc 100644
--- a/source/backend/api/Areas/Projects/Controllers/ProjectController.cs
+++ b/source/backend/api/Areas/Projects/Controllers/ProjectController.cs
@@ -3,15 +3,15 @@
using MapsterMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Pims.Core.Api.Exceptions;
using Pims.Api.Models.Concepts.Product;
using Pims.Api.Models.Concepts.Project;
-using Pims.Core.Api.Policies;
using Pims.Api.Services;
+using Pims.Core.Api.Exceptions;
+using Pims.Core.Api.Policies;
using Pims.Core.Exceptions;
using Pims.Core.Json;
-using Pims.Dal.Exceptions;
using Pims.Core.Security;
+using Pims.Dal.Exceptions;
using Swashbuckle.AspNetCore.Annotations;
namespace Pims.Api.Areas.Projects.Controllers
diff --git a/source/backend/api/Areas/Property/Controllers/PropertyController.cs b/source/backend/api/Areas/Property/Controllers/PropertyController.cs
index 42475cb076..4563a00e01 100644
--- a/source/backend/api/Areas/Property/Controllers/PropertyController.cs
+++ b/source/backend/api/Areas/Property/Controllers/PropertyController.cs
@@ -3,11 +3,11 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Pims.Api.Models.Concepts.Property;
-using Pims.Core.Api.Policies;
using Pims.Api.Services;
+using Pims.Core.Api.Policies;
using Pims.Core.Json;
-using Pims.Dal.Repositories;
using Pims.Core.Security;
+using Pims.Dal.Repositories;
using Swashbuckle.AspNetCore.Annotations;
namespace Pims.Api.Areas.Property.Controllers
diff --git a/source/backend/api/Areas/Property/Controllers/PropertyManagementController.cs b/source/backend/api/Areas/Property/Controllers/PropertyManagementController.cs
index 708d394477..f4b76a76c4 100644
--- a/source/backend/api/Areas/Property/Controllers/PropertyManagementController.cs
+++ b/source/backend/api/Areas/Property/Controllers/PropertyManagementController.cs
@@ -2,8 +2,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Pims.Api.Models.Concepts.Property;
-using Pims.Core.Api.Policies;
using Pims.Api.Services;
+using Pims.Core.Api.Policies;
using Pims.Core.Json;
using Pims.Core.Security;
using Swashbuckle.AspNetCore.Annotations;
diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj
index a5788ba515..e3d29a385e 100644
--- a/source/backend/api/Pims.Api.csproj
+++ b/source/backend/api/Pims.Api.csproj
@@ -2,8 +2,8 @@
0ef6255f-9ea0-49ec-8c65-c172304b4926
- 5.7.3-96.22
- 5.7.3.96
+ 5.8.0-98.21
+ 5.8.0.98
true
{16BC0468-78F6-4C91-87DA-7403C919E646}
net8.0
diff --git a/source/backend/api/Repositories/Mayan/MayanAuthRepository.cs b/source/backend/api/Repositories/Mayan/MayanAuthRepository.cs
index 952ba6b4d4..dd4389abdc 100644
--- a/source/backend/api/Repositories/Mayan/MayanAuthRepository.cs
+++ b/source/backend/api/Repositories/Mayan/MayanAuthRepository.cs
@@ -29,7 +29,7 @@ public class MayanAuthRepository : MayanBaseRepository, IEdmsAuthRepository
/// The injected configuration provider.
/// The jsonOptions.
public MayanAuthRepository(
- ILogger logger,
+ ILogger logger,
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
IOptions jsonOptions)
diff --git a/source/backend/api/Repositories/Mayan/MayanDocumentRepository.cs b/source/backend/api/Repositories/Mayan/MayanDocumentRepository.cs
index 72a3315968..2ca2dcaec3 100644
--- a/source/backend/api/Repositories/Mayan/MayanDocumentRepository.cs
+++ b/source/backend/api/Repositories/Mayan/MayanDocumentRepository.cs
@@ -4,6 +4,7 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
+using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -160,11 +161,9 @@ public async Task> TryUpdateDocumentTypeAsync(long docu
string authenticationToken = await _authRepository.GetTokenAsync();
Uri endpoint = new($"{this._config.BaseUri}/documents/{documentId}/type/change/");
- using MultipartFormDataContent multiContent = new MultipartFormDataContent();
- using HttpContent content = new StringContent(documentTypeId.ToString(CultureInfo.InvariantCulture));
- multiContent.Add(content, "document_type_id");
- var response = await PostAsync(endpoint, multiContent, authenticationToken).ConfigureAwait(true);
+ using var content = new StringContent($"{{ \"document_type_id\": \"{documentTypeId}\" }}", Encoding.UTF8, "application/json");
+ var response = await PostAsync(endpoint, content, authenticationToken).ConfigureAwait(true);
_logger.LogDebug("Finished updating document type for document {documentId}", documentId);
return response;
diff --git a/source/backend/api/Services/AcquisitionFileService.cs b/source/backend/api/Services/AcquisitionFileService.cs
index aa059732d6..dcd46d0b10 100644
--- a/source/backend/api/Services/AcquisitionFileService.cs
+++ b/source/backend/api/Services/AcquisitionFileService.cs
@@ -39,6 +39,7 @@ public class AcquisitionFileService : IAcquisitionFileService
private readonly IAcquisitionStatusSolver _statusSolver;
private readonly IPropertyService _propertyService;
private readonly IProjectRepository _projectRepository;
+ private readonly IPropertyOperationService _propertyOperationService;
public AcquisitionFileService(
ClaimsPrincipal user,
@@ -58,7 +59,8 @@ public AcquisitionFileService(
ITakeRepository takeRepository,
IProjectRepository projectRepository,
IAcquisitionStatusSolver statusSolver,
- IPropertyService propertyService)
+ IPropertyService propertyService,
+ IPropertyOperationService propertyOperationService)
{
_user = user;
_logger = logger;
@@ -78,6 +80,7 @@ public AcquisitionFileService(
_statusSolver = statusSolver;
_propertyService = propertyService;
_projectRepository = projectRepository;
+ _propertyOperationService = propertyOperationService;
}
public Paged GetPage(AcquisitionFilter filter)
@@ -327,6 +330,7 @@ public PimsAcquisitionFile UpdateProperties(PimsAcquisitionFile acquisitionFile,
{
incomingAcquisitionProperty.Internal_Id = matchingProperty.Internal_Id;
}
+
// If the property is not new, check if the name has been updated.
if (incomingAcquisitionProperty.Internal_Id != 0)
{
@@ -364,7 +368,7 @@ public PimsAcquisitionFile UpdateProperties(PimsAcquisitionFile acquisitionFile,
foreach (var deletedProperty in differenceSet)
{
var acqFileProperties = _acquisitionFilePropertyRepository.GetPropertiesByAcquisitionFileId(acquisitionFile.Internal_Id).FirstOrDefault(ap => ap.PropertyId == deletedProperty.PropertyId);
- if (acqFileProperties.PimsTakes.Any() || acqFileProperties.PimsInthldrPropInterests.Any())
+ if (acqFileProperties.PimsTakes.Count > 0 || acqFileProperties.PimsInthldrPropInterests.Count > 0)
{
throw new BusinessRuleViolationException("You must remove all takes and interest holders from an acquisition file property before removing that property from an acquisition file");
}
@@ -374,6 +378,11 @@ public PimsAcquisitionFile UpdateProperties(PimsAcquisitionFile acquisitionFile,
throw new BusinessRuleViolationException("Acquisition File property can not be removed since it's assigned as a property for a compensation requisition");
}
+ if (_propertyOperationService.GetOperationsForProperty(deletedProperty.PropertyId).Count > 0)
+ {
+ throw new BusinessRuleViolationException("This property cannot be deleted because it is part of a subdivision or consolidation");
+ }
+
_acquisitionFilePropertyRepository.Delete(deletedProperty);
var totalAssociationCount = _propertyRepository.GetAllAssociationsCountById(deletedProperty.PropertyId);
@@ -835,34 +844,35 @@ private void ValidatePayeeDependency(PimsAcquisitionFile acquisitionFile)
{
var currentAcquisitionFile = _acqFileRepository.GetById(acquisitionFile.Internal_Id);
var compensationRequisitions = _compensationRequisitionRepository.GetAllByAcquisitionFileId(acquisitionFile.Internal_Id);
+ var compReqPayees = compensationRequisitions.SelectMany(x => x.PimsCompReqPayees).ToList();
- if (compensationRequisitions.Count == 0)
+ if (compReqPayees.Count == 0)
{
return;
}
- foreach (var compReq in compensationRequisitions)
+ foreach (var payee in compReqPayees)
{
// Check for Acquisition File Owner removed
- if (compReq.AcquisitionOwnerId is not null
- && !acquisitionFile.PimsAcquisitionOwners.Any(x => x.Internal_Id.Equals(compReq.AcquisitionOwnerId))
- && currentAcquisitionFile.PimsAcquisitionOwners.Any(x => x.Internal_Id.Equals(compReq.AcquisitionOwnerId)))
+ if (payee.AcquisitionOwnerId is not null
+ && !acquisitionFile.PimsAcquisitionOwners.Any(x => x.Internal_Id.Equals(payee.AcquisitionOwnerId))
+ && currentAcquisitionFile.PimsAcquisitionOwners.Any(x => x.Internal_Id.Equals(payee.AcquisitionOwnerId)))
{
throw new ForeignKeyDependencyException("Acquisition File Owner can not be removed since it's assigned as a payee for a compensation requisition");
}
- // Check for Acquisition InterestHolders
- if (compReq.InterestHolderId is not null
- && !acquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(compReq.InterestHolderId))
- && currentAcquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(compReq.InterestHolderId)))
+ //// Check for Acquisition InterestHolders
+ if (payee.InterestHolderId is not null
+ && !acquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(payee.InterestHolderId))
+ && currentAcquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(payee.InterestHolderId)))
{
throw new ForeignKeyDependencyException("Acquisition File Interest Holders can not be removed since it's assigned as a payee for a compensation requisition");
}
- // Check for File Person
- if (compReq.AcquisitionFileTeamId is not null
- && !acquisitionFile.PimsAcquisitionFileTeams.Any(x => x.Internal_Id.Equals(compReq.AcquisitionFileTeamId))
- && currentAcquisitionFile.PimsAcquisitionFileTeams.Any(x => x.Internal_Id.Equals(compReq.AcquisitionFileTeamId)))
+ //// Check for File Person
+ if (payee.AcquisitionFileTeamId is not null
+ && !acquisitionFile.PimsAcquisitionFileTeams.Any(x => x.Internal_Id.Equals(payee.AcquisitionFileTeamId))
+ && currentAcquisitionFile.PimsAcquisitionFileTeams.Any(x => x.Internal_Id.Equals(payee.AcquisitionFileTeamId)))
{
throw new ForeignKeyDependencyException("Acquisition File team member can not be removed since it's assigned as a payee for a compensation requisition");
}
@@ -873,18 +883,19 @@ private void ValidateInterestHoldersDependency(long acquisitionFileId, List x.PimsCompReqPayees).ToList();
- if (compensationRequisitions.Count == 0)
+ if (compReqPayees.Count == 0)
{
return;
}
- foreach (var compReq in compensationRequisitions)
+ foreach (var payee in compReqPayees)
{
// Check for Interest Holder
- if (compReq.InterestHolderId is not null
- && !interestHolders.Any(x => x.InterestHolderId.Equals(compReq.InterestHolderId))
- && currentAcquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(compReq.InterestHolderId)))
+ if (payee.InterestHolderId is not null
+ && !interestHolders.Any(x => x.InterestHolderId.Equals(payee.InterestHolderId))
+ && currentAcquisitionFile.PimsInterestHolders.Any(x => x.Internal_Id.Equals(payee.InterestHolderId)))
{
throw new ForeignKeyDependencyException("Acquisition File Interest Holder can not be removed since it's assigned as a payee for a compensation requisition");
}
diff --git a/source/backend/api/Services/CompensationRequisitionService.cs b/source/backend/api/Services/CompensationRequisitionService.cs
index 74ae0bd5a2..f2c51ba758 100644
--- a/source/backend/api/Services/CompensationRequisitionService.cs
+++ b/source/backend/api/Services/CompensationRequisitionService.cs
@@ -150,12 +150,20 @@ public IEnumerable GetFileCompensationRequisitions(
return compReqs;
}
- public IEnumerable GetCompensationRequisitionFinancials(long id)
+ public IEnumerable GetCompensationRequisitionFinancials(long compReqId)
{
- _logger.LogInformation("Getting compensations financials for id: {acquisitionFileId}", id);
+ _logger.LogInformation("Getting compensations financials for id: {compReqId}", compReqId);
_user.ThrowIfNotAuthorized(Permissions.CompensationRequisitionView);
- return _compensationRequisitionRepository.GetCompensationRequisitionFinancials(id);
+ return _compensationRequisitionRepository.GetCompensationRequisitionFinancials(compReqId);
+ }
+
+ public IEnumerable GetCompensationRequisitionPayees(long compReqId)
+ {
+ _logger.LogInformation("Getting compensations payees for id: {compReqId}", compReqId);
+ _user.ThrowIfNotAuthorized(Permissions.CompensationRequisitionView);
+
+ return _compensationRequisitionRepository.GetCompensationRequisitionPayees(compReqId);
}
private static string GetCompensationRequisitionStatusText(bool? isDraft)
diff --git a/source/backend/api/Services/DispositionFileService.cs b/source/backend/api/Services/DispositionFileService.cs
index 89b870e499..2872b301f0 100644
--- a/source/backend/api/Services/DispositionFileService.cs
+++ b/source/backend/api/Services/DispositionFileService.cs
@@ -6,18 +6,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pims.Api.Constants;
-using Pims.Core.Api.Exceptions;
using Pims.Api.Helpers.Extensions;
using Pims.Api.Models.CodeTypes;
+using Pims.Core.Api.Exceptions;
using Pims.Core.Exceptions;
using Pims.Core.Extensions;
+using Pims.Core.Security;
using Pims.Dal.Entities;
using Pims.Dal.Entities.Extensions;
using Pims.Dal.Entities.Models;
using Pims.Dal.Exceptions;
using Pims.Dal.Helpers.Extensions;
using Pims.Dal.Repositories;
-using Pims.Core.Security;
namespace Pims.Api.Services
{
@@ -34,6 +34,7 @@ public class DispositionFileService : IDispositionFileService
private readonly IDispositionFileChecklistRepository _checklistRepository;
private readonly IEntityNoteRepository _entityNoteRepository;
private readonly IDispositionStatusSolver _dispositionStatusSolver;
+ private readonly IPropertyOperationService _propertyOperationService;
public DispositionFileService(
ClaimsPrincipal user,
@@ -47,7 +48,8 @@ public DispositionFileService(
IDispositionFileChecklistRepository checklistRepository,
IEntityNoteRepository entityNoteRepository,
IUserRepository userRepository,
- IDispositionStatusSolver dispositionStatusSolver)
+ IDispositionStatusSolver dispositionStatusSolver,
+ IPropertyOperationService propertyOperationService)
{
_user = user;
_logger = logger;
@@ -60,6 +62,7 @@ public DispositionFileService(
_entityNoteRepository = entityNoteRepository;
_userRepository = userRepository;
_dispositionStatusSolver = dispositionStatusSolver;
+ _propertyOperationService = propertyOperationService;
}
public PimsDispositionFile Add(PimsDispositionFile dispositionFile, IEnumerable userOverrides)
@@ -506,6 +509,7 @@ public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile,
{
incomingDispositionProperty.Internal_Id = matchingProperty.Internal_Id;
}
+
// If the property is not new, check if the name has been updated.
if (incomingDispositionProperty.Internal_Id != 0)
{
@@ -542,6 +546,11 @@ public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile,
List differenceSet = currentFileProperties.Where(x => !dispositionFile.PimsDispositionFileProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList();
foreach (var deletedProperty in differenceSet)
{
+ if (_propertyOperationService.GetOperationsForProperty(deletedProperty.PropertyId).Count > 0)
+ {
+ throw new BusinessRuleViolationException("This property cannot be deleted because it is part of a subdivision or consolidation");
+ }
+
_dispositionFilePropertyRepository.Delete(deletedProperty);
var totalAssociationCount = _propertyRepository.GetAllAssociationsCountById(deletedProperty.PropertyId);
diff --git a/source/backend/api/Services/DocumentQueueService.cs b/source/backend/api/Services/DocumentQueueService.cs
index 03d2d3263e..b000931231 100644
--- a/source/backend/api/Services/DocumentQueueService.cs
+++ b/source/backend/api/Services/DocumentQueueService.cs
@@ -156,6 +156,11 @@ public async Task PollForDocument(PimsDocumentQueue documentQ
this.Logger.LogError("Document Queue {documentQueueId} is not in valid state, aborting poll.", documentQueue.DocumentQueueId);
return databaseDocumentQueue;
}
+ else if (databaseDocumentQueue.DocumentQueueStatusTypeCode == DocumentQueueStatusTypes.PENDING.ToString() || databaseDocumentQueue.DocumentQueueStatusTypeCode == DocumentQueueStatusTypes.SUCCESS.ToString())
+ {
+ this.Logger.LogError("Document Queue {documentQueueId} is not in valid state, aborting poll.", documentQueue.DocumentQueueId);
+ return databaseDocumentQueue;
+ }
var relatedDocument = _documentRepository.TryGet(documentQueue.DocumentId.Value);
diff --git a/source/backend/api/Services/ICompensationRequisitionService.cs b/source/backend/api/Services/ICompensationRequisitionService.cs
index 884cbae6f1..d99d24979d 100644
--- a/source/backend/api/Services/ICompensationRequisitionService.cs
+++ b/source/backend/api/Services/ICompensationRequisitionService.cs
@@ -20,6 +20,8 @@ public interface ICompensationRequisitionService
PimsCompensationRequisition AddCompensationRequisition(FileTypes fileType, PimsCompensationRequisition compensationRequisition);
- IEnumerable GetCompensationRequisitionFinancials(long id);
+ IEnumerable GetCompensationRequisitionFinancials(long compReqId);
+
+ IEnumerable GetCompensationRequisitionPayees(long compReqId);
}
}
diff --git a/source/backend/api/Services/LeaseService.cs b/source/backend/api/Services/LeaseService.cs
index 14d550a1b7..ddf62eda38 100644
--- a/source/backend/api/Services/LeaseService.cs
+++ b/source/backend/api/Services/LeaseService.cs
@@ -38,6 +38,7 @@ public class LeaseService : BaseService, ILeaseService
private readonly IPropertyService _propertyService;
private readonly ILookupRepository _lookupRepository;
private readonly ICompReqFinancialService _compReqFinancialService;
+ private readonly IPropertyOperationService _propertyOperationService;
public LeaseService(
ClaimsPrincipal user,
@@ -55,7 +56,8 @@ public LeaseService(
IUserRepository userRepository,
IPropertyService propertyService,
ILookupRepository lookupRepository,
- ICompReqFinancialService compReqFinancialService)
+ ICompReqFinancialService compReqFinancialService,
+ IPropertyOperationService propertyOperationService)
: base(user, logger)
{
_logger = logger;
@@ -74,6 +76,7 @@ public LeaseService(
_propertyService = propertyService;
_lookupRepository = lookupRepository;
_compReqFinancialService = compReqFinancialService;
+ _propertyOperationService = propertyOperationService;
}
public PimsLease GetById(long leaseId)
@@ -255,6 +258,7 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr
{
incomingLeaseProperty.Internal_Id = matchingProperty.Internal_Id;
}
+
// If the property is not new, check if the marker location has been updated.
if (incomingLeaseProperty.Internal_Id != 0)
{
@@ -287,6 +291,11 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr
throw new BusinessRuleViolationException("Lease File property can not be removed since it's assigned as a property for a compensation requisition");
}
+ if (_propertyOperationService.GetOperationsForProperty(deletedProperty.PropertyId).Count > 0)
+ {
+ throw new BusinessRuleViolationException("This property cannot be deleted because it is part of a subdivision or consolidation");
+ }
+
var totalAssociationCount = _propertyRepository.GetAllAssociationsCountById(deletedProperty.PropertyId);
if (totalAssociationCount <= 1)
{
diff --git a/source/backend/api/Services/ProjectService.cs b/source/backend/api/Services/ProjectService.cs
index 4519051c1d..bb6e0019e4 100644
--- a/source/backend/api/Services/ProjectService.cs
+++ b/source/backend/api/Services/ProjectService.cs
@@ -4,6 +4,7 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
+using Pims.Core.Api.Exceptions;
using Pims.Core.Api.Services;
using Pims.Core.Exceptions;
using Pims.Core.Extensions;
@@ -140,6 +141,8 @@ public PimsProject Add(PimsProject project, IEnumerable userOv
throw new BusinessRuleViolationException($"Project {project.Code} already exists. Project will not be duplicated.");
}
+ ValidateTeamMembers(project);
+
var externalProducts = MatchProducts(project);
if (externalProducts.Count > 0 && !userOverrides.Contains(UserOverrideCode.ProductReuse))
{
@@ -159,6 +162,8 @@ public PimsProject Update(PimsProject project, IEnumerable use
project.ThrowIfNull(nameof(project));
_logger.LogInformation($"Updating project with id ${project.Internal_Id}");
+ ValidateTeamMembers(project);
+
var externalProducts = MatchProducts(project);
if (externalProducts.Count > 0 && !userOverrides.Contains(UserOverrideCode.ProductReuse))
{
@@ -174,6 +179,15 @@ public PimsProject Update(PimsProject project, IEnumerable use
return updatedProject;
}
+ private static void ValidateTeamMembers(PimsProject project)
+ {
+ bool duplicate = project.PimsProjectPeople.GroupBy(p => p.PersonId).Any(g => g.Count() > 1);
+ if (duplicate)
+ {
+ throw new BadRequestException("Invalid Project management team, each team member can only be added once.");
+ }
+ }
+
/*
* Updates the passed project with the matched products in place.
* Note: The return list contains the external products matched
diff --git a/source/backend/api/Services/PropertyOperationService.cs b/source/backend/api/Services/PropertyOperationService.cs
index 4d8c8e80a7..8327482d7e 100644
--- a/source/backend/api/Services/PropertyOperationService.cs
+++ b/source/backend/api/Services/PropertyOperationService.cs
@@ -4,10 +4,10 @@
using Microsoft.Extensions.Logging;
using Pims.Core.Api.Exceptions;
using Pims.Core.Exceptions;
-using Pims.Dal.Entities;
using Pims.Core.Extensions;
-using Pims.Dal.Repositories;
using Pims.Core.Security;
+using Pims.Dal.Entities;
+using Pims.Dal.Repositories;
namespace Pims.Api.Services
{
diff --git a/source/backend/api/Services/PropertyService.cs b/source/backend/api/Services/PropertyService.cs
index 6028afcd99..058452f41b 100644
--- a/source/backend/api/Services/PropertyService.cs
+++ b/source/backend/api/Services/PropertyService.cs
@@ -132,7 +132,7 @@ public PimsProperty RetireProperty(PimsProperty property, bool commitTransaction
{
_propertyRepository.CommitTransaction();
}
- return GetById(retiredProperty.Internal_Id); ;
+ return GetById(retiredProperty.Internal_Id);
}
public IList GetContacts(long propertyId)
@@ -222,9 +222,9 @@ private static void PropertyHasActiveLease(IEnumerable proper
hasActiveExpiryDate = false;
List activeLeaseList = propertyLeases.Select(x => x.Lease).Where(y => y.LeaseStatusTypeCode == LeaseStatusTypes.ACTIVE.ToString()).ToList();
- foreach(var agreement in activeLeaseList)
+ foreach (var agreement in activeLeaseList)
{
- if(!agreement.TerminationDate.HasValue)
+ if (!agreement.TerminationDate.HasValue)
{
var latestRenewal = agreement.PimsLeaseRenewals.Where(x => x.IsExercised == true).OrderByDescending(x => x.CommencementDt).FirstOrDefault();
if (latestRenewal is null) // No Renewal - Check only Lease dates.
diff --git a/source/backend/api/Services/ResearchFileService.cs b/source/backend/api/Services/ResearchFileService.cs
index 81b66e5e67..8d52a8146f 100644
--- a/source/backend/api/Services/ResearchFileService.cs
+++ b/source/backend/api/Services/ResearchFileService.cs
@@ -4,6 +4,7 @@
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
+using Pims.Core.Exceptions;
using Pims.Core.Extensions;
using Pims.Core.Security;
using Pims.Dal.Entities;
@@ -23,6 +24,7 @@ public class ResearchFileService : IResearchFileService
private readonly ILookupRepository _lookupRepository;
private readonly IEntityNoteRepository _entityNoteRepository;
private readonly IPropertyService _propertyService;
+ private readonly IPropertyOperationService _propertyOperationService;
public ResearchFileService(
ClaimsPrincipal user,
@@ -30,10 +32,10 @@ public ResearchFileService(
IResearchFileRepository researchFileRepository,
IResearchFilePropertyRepository researchFilePropertyRepository,
IPropertyRepository propertyRepository,
- ICoordinateTransformService coordinateService,
ILookupRepository lookupRepository,
IEntityNoteRepository entityNoteRepository,
- IPropertyService propertyService)
+ IPropertyService propertyService,
+ IPropertyOperationService propertyOperationService)
{
_user = user;
_logger = logger;
@@ -43,6 +45,7 @@ public ResearchFileService(
_lookupRepository = lookupRepository;
_entityNoteRepository = entityNoteRepository;
_propertyService = propertyService;
+ _propertyOperationService = propertyOperationService;
}
public PimsResearchFile GetById(long id)
@@ -156,6 +159,11 @@ public PimsResearchFile UpdateProperties(PimsResearchFile researchFile, IEnumera
List differenceSet = currentFileProperties.Where(x => !researchFile.PimsPropertyResearchFiles.Any(y => y.Internal_Id == x.Internal_Id)).ToList();
foreach (var deletedProperty in differenceSet)
{
+ if (_propertyOperationService.GetOperationsForProperty(deletedProperty.PropertyId).Count > 0)
+ {
+ throw new BusinessRuleViolationException("This property cannot be deleted because it is part of a subdivision or consolidation");
+ }
+
_researchFilePropertyRepository.Delete(deletedProperty);
var totalAssociationCount = _propertyRepository.GetAllAssociationsCountById(deletedProperty.PropertyId);
if (totalAssociationCount <= 1)
diff --git a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeMap.cs b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeMap.cs
new file mode 100644
index 0000000000..1fbb004ff7
--- /dev/null
+++ b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeMap.cs
@@ -0,0 +1,31 @@
+using Mapster;
+using Pims.Api.Models.Base;
+using Entity = Pims.Dal.Entities;
+
+namespace Pims.Api.Models.Concepts.CompensationRequisition
+{
+ public class CompReqPayeeMap : IRegister
+ {
+ public void Register(TypeAdapterConfig config)
+ {
+ config.NewConfig()
+ .Map(dest => dest.CompReqPayeeId, src => src.CompReqPayeeId)
+ .Map(dest => dest.CompensationRequisitionId, src => src.CompensationRequisitionId)
+ .Map(dest => dest.AcquisitionFileTeamId, src => src.AcquisitionFileTeamId)
+ .Map(dest => dest.AcquisitionFileTeam, src => src.AcquisitionFileTeam)
+ .Map(dest => dest.AcquisitionOwnerId, src => src.AcquisitionOwnerId)
+ .Map(dest => dest.AcquisitionOwner, src => src.AcquisitionOwner)
+ .Map(dest => dest.InterestHolderId, src => src.InterestHolderId)
+ .Map(dest => dest.InterestHolder, src => src.InterestHolder)
+ .Inherits();
+
+ config.NewConfig()
+ .Map(dest => dest.CompReqPayeeId, src => src.CompReqPayeeId)
+ .Map(dest => dest.CompensationRequisitionId, src => src.CompensationRequisitionId)
+ .Map(dest => dest.AcquisitionFileTeamId, src => src.AcquisitionFileTeamId)
+ .Map(dest => dest.AcquisitionOwnerId, src => src.AcquisitionOwnerId)
+ .Map(dest => dest.InterestHolderId, src => src.InterestHolderId)
+ .Inherits();
+ }
+ }
+}
diff --git a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeModel.cs b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeModel.cs
new file mode 100644
index 0000000000..76b18a6d4b
--- /dev/null
+++ b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompReqPayeeModel.cs
@@ -0,0 +1,27 @@
+using Pims.Api.Models.Base;
+using Pims.Api.Models.Concepts.AcquisitionFile;
+using Pims.Api.Models.Concepts.InterestHolder;
+
+namespace Pims.Api.Models.Concepts.CompensationRequisition
+{
+ public class CompReqPayeeModel : BaseAuditModel
+ {
+ public long? CompReqPayeeId { get; set; }
+
+ public long? CompensationRequisitionId { get; set; }
+
+ public CompensationRequisitionModel CompensationRequisition { get; set; }
+
+ public long? AcquisitionOwnerId { get; set; }
+
+ public AcquisitionFileOwnerModel AcquisitionOwner { get; set; }
+
+ public long? InterestHolderId { get; set; }
+
+ public InterestHolderModel InterestHolder { get; set; }
+
+ public long? AcquisitionFileTeamId { get; set; }
+
+ public AcquisitionFileTeamModel AcquisitionFileTeam { get; set; }
+ }
+}
diff --git a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionMap.cs b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionMap.cs
index d87ffd2158..278c26d758 100644
--- a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionMap.cs
+++ b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionMap.cs
@@ -1,5 +1,6 @@
using Mapster;
using Pims.Api.Models.Base;
+using System.Linq;
using Entity = Pims.Dal.Entities;
namespace Pims.Api.Models.Concepts.CompensationRequisition
@@ -23,16 +24,8 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.Responsibility, src => src.Responsibility)
.Map(dest => dest.FinalizedDate, src => src.FinalizedDate)
.Map(dest => dest.AgreementDate, src => src.AgreementDt)
- .Map(dest => dest.ExpropriationNoticeServedDate, src => src.ExpropNoticeServedDt)
- .Map(dest => dest.ExpropriationVestingDate, src => src.ExpropVestingDt)
.Map(dest => dest.GenerationDate, src => src.GenerationDt)
.Map(dest => dest.Financials, src => src.PimsCompReqFinancials)
- .Map(dest => dest.AcquisitionOwnerId, src => src.AcquisitionOwnerId)
- .Map(dest => dest.AcquisitionOwner, src => src.AcquisitionOwner)
- .Map(dest => dest.InterestHolderId, src => src.InterestHolderId)
- .Map(dest => dest.InterestHolder, src => src.InterestHolder)
- .Map(dest => dest.AcquisitionFileTeamId, src => src.AcquisitionFileTeamId)
- .Map(dest => dest.AcquisitionFileTeam, src => src.AcquisitionFileTeam)
.Map(dest => dest.LegacyPayee, src => src.LegacyPayee)
.Map(dest => dest.IsPaymentInTrust, src => src.IsPaymentInTrust)
.Map(dest => dest.GstNumber, src => src.GstNumber)
@@ -41,9 +34,10 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.DetailedRemarks, src => src.DetailedRemarks)
.Map(dest => dest.AlternateProjectId, src => src.AlternateProjectId)
.Map(dest => dest.AlternateProject, src => src.AlternateProject)
- .Map(dest => dest.CompReqLeaseStakeholder, src => src.PimsLeaseStakeholderCompReqs)
+ .Map(dest => dest.CompReqLeaseStakeholders, src => src.PimsLeaseStakeholderCompReqs)
.Map(dest => dest.CompReqAcquisitionProperties, src => src.PimsPropAcqFlCompReqs)
.Map(dest => dest.CompReqLeaseProperties, src => src.PimsPropLeaseCompReqs)
+ .Map(dest => dest.CompReqPayees, src => src.PimsCompReqPayees)
.Inherits();
config.NewConfig()
@@ -57,13 +51,8 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.ResponsibilityId, src => src.ResponsibilityId)
.Map(dest => dest.FinalizedDate, src => src.FinalizedDate)
.Map(dest => dest.AgreementDt, src => src.AgreementDate)
- .Map(dest => dest.ExpropNoticeServedDt, src => src.ExpropriationNoticeServedDate)
- .Map(dest => dest.ExpropVestingDt, src => src.ExpropriationVestingDate)
.Map(dest => dest.GenerationDt, src => src.GenerationDate)
.Map(dest => dest.PimsCompReqFinancials, src => src.Financials)
- .Map(dest => dest.AcquisitionOwnerId, src => src.AcquisitionOwnerId)
- .Map(dest => dest.InterestHolderId, src => src.InterestHolderId)
- .Map(dest => dest.AcquisitionFileTeamId, src => src.AcquisitionFileTeamId)
.Map(dest => dest.LegacyPayee, src => src.LegacyPayee)
.Map(dest => dest.IsPaymentInTrust, src => src.IsPaymentInTrust)
.Map(dest => dest.GstNumber, src => src.GstNumber)
@@ -72,9 +61,10 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.DetailedRemarks, src => src.DetailedRemarks)
.Map(dest => dest.AlternateProjectId, src => src.AlternateProjectId)
.Map(dest => dest.AlternateProject, src => src.AlternateProject)
- .Map(dest => dest.PimsLeaseStakeholderCompReqs, src => src.CompReqLeaseStakeholder)
+ .Map(dest => dest.PimsLeaseStakeholderCompReqs, src => src.CompReqLeaseStakeholders)
.Map(dest => dest.PimsPropAcqFlCompReqs, src => src.CompReqAcquisitionProperties)
.Map(dest => dest.PimsPropLeaseCompReqs, src => src.CompReqLeaseProperties)
+ .Map(dest => dest.PimsCompReqPayees, src => src.CompReqPayees)
.Inherits();
}
}
diff --git a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionModel.cs b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionModel.cs
index 4a8f276ce7..ee9dfc3675 100644
--- a/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionModel.cs
+++ b/source/backend/apimodels/Models/Concepts/CompensationRequisition/CompensationRequisitionModel.cs
@@ -3,7 +3,6 @@
using Pims.Api.Models.Base;
using Pims.Api.Models.Concepts.AcquisitionFile;
using Pims.Api.Models.Concepts.FinancialCode;
-using Pims.Api.Models.Concepts.InterestHolder;
using Pims.Api.Models.Concepts.Project;
namespace Pims.Api.Models.Concepts.CompensationRequisition
@@ -38,26 +37,10 @@ public class CompensationRequisitionModel : BaseAuditModel
public DateOnly? AgreementDate { get; set; }
- public DateOnly? ExpropriationNoticeServedDate { get; set; }
-
- public DateOnly? ExpropriationVestingDate { get; set; }
-
public DateOnly? GenerationDate { get; set; }
public List Financials { get; set; }
- public long? AcquisitionOwnerId { get; set; }
-
- public AcquisitionFileOwnerModel AcquisitionOwner { get; set; }
-
- public long? InterestHolderId { get; set; }
-
- public InterestHolderModel InterestHolder { get; set; }
-
- public long? AcquisitionFileTeamId { get; set; }
-
- public AcquisitionFileTeamModel AcquisitionFileTeam { get; set; }
-
public string LegacyPayee { get; set; }
public bool? IsPaymentInTrust { get; set; }
@@ -72,7 +55,9 @@ public class CompensationRequisitionModel : BaseAuditModel
public ProjectModel AlternateProject { get; set; }
- public IEnumerable CompReqLeaseStakeholder { get; set; }
+ public IEnumerable CompReqPayees { get; set; }
+
+ public IEnumerable CompReqLeaseStakeholders { get; set; }
public IEnumerable CompReqAcquisitionProperties { get; set; }
diff --git a/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentMap.cs b/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentMap.cs
index f585c5977e..e2b5247d87 100644
--- a/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentMap.cs
+++ b/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentMap.cs
@@ -17,6 +17,7 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.InterestHolder, src => src.InterestHolder)
.Map(dest => dest.ExpropriatingAuthorityId, src => src.ExpropriatingAuthority)
.Map(dest => dest.ExpropriatingAuthority, src => src.ExpropriatingAuthorityNavigation)
+ .Map(dest => dest.AdvancedPaymentServedDate, src => src.AdvPmtServedDt)
.Map(dest => dest.Description, src => src.Description)
.Map(dest => dest.IsDisabled, src => src.IsDisabled)
.Map(dest => dest.RowVersion, src => src.ConcurrencyControlNumber)
@@ -29,6 +30,7 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.AcquisitionOwnerId, src => src.AcquisitionOwnerId)
.Map(dest => dest.InterestHolderId, src => src.InterestHolderId)
.Map(dest => dest.ExpropriatingAuthority, src => src.ExpropriatingAuthorityId)
+ .Map(dest => dest.AdvPmtServedDt, src => src.AdvancedPaymentServedDate)
.Map(dest => dest.Description, src => src.Description)
.Map(dest => dest.IsDisabled, src => src.IsDisabled)
.Map(dest => dest.ConcurrencyControlNumber, src => src.RowVersion)
diff --git a/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentModel.cs b/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentModel.cs
index d172323e7f..b782fcbd35 100644
--- a/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentModel.cs
+++ b/source/backend/apimodels/Models/Concepts/ExpropriationPayment/ExpropriationPaymentModel.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using Pims.Api.Models.Base;
using Pims.Api.Models.Concepts.AcquisitionFile;
@@ -24,6 +25,8 @@ public class ExpropriationPaymentModel : BaseAuditModel
public OrganizationModel ExpropriatingAuthority { get; set; }
+ public DateOnly? AdvancedPaymentServedDate { get; set; }
+
public string Description { get; set; }
public bool? IsDisabled { get; set; }
diff --git a/source/backend/apimodels/Models/Requests/Document/Upload/DocumentUploadRequest.cs b/source/backend/apimodels/Models/Requests/Document/Upload/DocumentUploadRequest.cs
index 7d08d437e5..3f03a1f930 100644
--- a/source/backend/apimodels/Models/Requests/Document/Upload/DocumentUploadRequest.cs
+++ b/source/backend/apimodels/Models/Requests/Document/Upload/DocumentUploadRequest.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.IO;
using Microsoft.AspNetCore.Http;
using Pims.Api.Models.Concepts.Document;
diff --git a/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs b/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs
index d0aaa708d2..48a4a19bbb 100644
--- a/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs
+++ b/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs
@@ -177,7 +177,7 @@ public async Task> DeleteAsync(Uri endpoint, string aut
break;
default:
result.Status = ExternalResponseStatus.Error;
- result.Message = $"Unable to contact endpoint {response.RequestMessage.RequestUri}. Http status {response.StatusCode}";
+ result.Message = $"Unable to contact endpoint {response.RequestMessage?.RequestUri}. Http status {response.StatusCode}";
break;
}
return result;
@@ -235,7 +235,7 @@ protected async Task> ProcessDownloadResp
break;
default:
result.Status = ExternalResponseStatus.Error;
- result.Message = $"Unable to contact endpoint {response.RequestMessage.RequestUri}. Http status {response.StatusCode}";
+ result.Message = $"Unable to contact endpoint {response.RequestMessage?.RequestUri}. Http status {response.StatusCode}";
break;
}
@@ -322,7 +322,10 @@ private async Task> ProcessResponse(HttpResponseMessage r
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.String:
- result.Payload = (T)Convert.ChangeType(payload, typeof(T), CultureInfo.InvariantCulture);
+ if(payload.Length > 0)
+ {
+ result.Payload = (T)Convert.ChangeType(payload, typeof(T), CultureInfo.InvariantCulture);
+ }
break;
default:
T requestTokenResult = JsonSerializer.Deserialize(payload, _jsonOptions.Value);
@@ -350,7 +353,7 @@ private async Task> ProcessResponse(HttpResponseMessage r
break;
default:
result.Status = ExternalResponseStatus.Error;
- result.Message = $"Unable to contact endpoint {response.RequestMessage.RequestUri}. Http status {response.StatusCode}";
+ result.Message = $"Unable to contact endpoint {response.RequestMessage?.RequestUri}. Http status {response.StatusCode}";
break;
}
diff --git a/source/backend/dal/Repositories/CompensationRequisitionRepository.cs b/source/backend/dal/Repositories/CompensationRequisitionRepository.cs
index 0ed6a06ffa..cc405e0db0 100644
--- a/source/backend/dal/Repositories/CompensationRequisitionRepository.cs
+++ b/source/backend/dal/Repositories/CompensationRequisitionRepository.cs
@@ -59,9 +59,7 @@ public PimsCompensationRequisition GetById(long compensationRequisitionId)
.Include(x => x.Responsibility)
.Include(c => c.PimsCompReqFinancials)
.ThenInclude(y => y.FinancialActivityCode)
- .Include(x => x.AcquisitionOwner)
- .Include(x => x.AcquisitionFileTeam)
- .Include(x => x.InterestHolder)
+ .Include(c => c.PimsCompReqPayees)
.Include(x => x.AlternateProject)
.Include(x => x.PimsLeaseStakeholderCompReqs)
.ThenInclude(y => y.LeaseStakeholder)
@@ -94,6 +92,7 @@ public PimsCompensationRequisition Update(PimsCompensationRequisition compensati
Context.UpdateChild(a => a.PimsPropAcqFlCompReqs, compensationRequisition.CompensationRequisitionId, compensationRequisition.PimsPropAcqFlCompReqs.ToArray(), true);
Context.UpdateChild(a => a.PimsPropLeaseCompReqs, compensationRequisition.CompensationRequisitionId, compensationRequisition.PimsPropLeaseCompReqs.ToArray(), true);
Context.UpdateChild(a => a.PimsLeaseStakeholderCompReqs, compensationRequisition.CompensationRequisitionId, compensationRequisition.PimsLeaseStakeholderCompReqs.ToArray(), true);
+ Context.UpdateChild(a => a.PimsCompReqPayees, compensationRequisition.CompensationRequisitionId, compensationRequisition.PimsCompReqPayees.ToArray(), true);
return compensationRequisition;
}
@@ -184,12 +183,31 @@ public List GetLeaseCompReqPropertiesById(long compensationRe
.ToList();
}
- public IEnumerable GetCompensationRequisitionFinancials(long id)
+ public IEnumerable GetCompensationRequisitionFinancials(long compReqId)
{
return Context.PimsCompReqFinancials
.AsNoTracking()
.Include(y => y.FinancialActivityCode)
- .Where(x => x.CompensationRequisitionId == id)
+ .Where(x => x.CompensationRequisitionId == compReqId)
+ .ToList();
+ }
+
+ public IEnumerable GetCompensationRequisitionPayees(long compReqId)
+ {
+ return Context.PimsCompReqPayees
+ .AsNoTracking()
+ .Include(x => x.AcquisitionOwner)
+ .Include(x => x.AcquisitionFileTeam)
+ .ThenInclude(y => y.Person)
+ .Include(x => x.AcquisitionFileTeam)
+ .ThenInclude(y => y.Organization)
+ .Include(x => x.InterestHolder)
+ .ThenInclude(y => y.InterestHolderTypeCodeNavigation)
+ .Include(x => x.InterestHolder)
+ .ThenInclude(y => y.Person)
+ .Include(x => x.InterestHolder)
+ .ThenInclude(y => y.Organization)
+ .Where(x => x.CompensationRequisitionId == compReqId)
.ToList();
}
}
diff --git a/source/backend/dal/Repositories/Interfaces/ICompensationRequisitionRepository.cs b/source/backend/dal/Repositories/Interfaces/ICompensationRequisitionRepository.cs
index a8f559b9f9..0228966c6f 100644
--- a/source/backend/dal/Repositories/Interfaces/ICompensationRequisitionRepository.cs
+++ b/source/backend/dal/Repositories/Interfaces/ICompensationRequisitionRepository.cs
@@ -21,6 +21,8 @@ public interface ICompensationRequisitionRepository : IRepository GetLeaseCompReqPropertiesById(long compensationRequisitionId);
- IEnumerable GetCompensationRequisitionFinancials(long id);
+ IEnumerable GetCompensationRequisitionFinancials(long compReqId);
+
+ IEnumerable GetCompensationRequisitionPayees(long compReqId);
}
}
diff --git a/source/backend/entities/Partials/PimsCompReqPayee.cs b/source/backend/entities/Partials/PimsCompReqPayee.cs
new file mode 100644
index 0000000000..13500ef14c
--- /dev/null
+++ b/source/backend/entities/Partials/PimsCompReqPayee.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Pims.Dal.Entities
+{
+
+ ///
+ /// PimsCompReqPayee class, provides an entity for the datamodel to manage the relationship between Compensation Requisitions and the payee.
+ ///
+ public partial class PimsCompReqPayee : StandardIdentityBaseAppEntity, IBaseAppEntity
+ {
+ [NotMapped]
+ public override long Internal_Id { get => CompReqPayeeId; set => CompReqPayeeId = value; }
+ }
+}
diff --git a/source/backend/entities/PimsBaseContext.cs b/source/backend/entities/PimsBaseContext.cs
index 2d81d1215b..bdd862a4a3 100644
--- a/source/backend/entities/PimsBaseContext.cs
+++ b/source/backend/entities/PimsBaseContext.cs
@@ -116,6 +116,10 @@ public PimsBaseContext(DbContextOptions options)
public virtual DbSet PimsCompReqFinancialHists { get; set; }
+ public virtual DbSet PimsCompReqPayees { get; set; }
+
+ public virtual DbSet PimsCompReqPayeeHists { get; set; }
+
public virtual DbSet PimsCompensationRequisitions { get; set; }
public virtual DbSet PimsCompensationRequisitionHists { get; set; }
@@ -2005,6 +2009,78 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.EffectiveDateHist).HasDefaultValueSql("(getutcdate())");
});
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.CompReqPayeeId).HasName("CMPRQP_PK");
+
+ entity.ToTable("PIMS_COMP_REQ_PAYEE", tb =>
+ {
+ tb.HasComment("Table to support multiple payees on a compensation requisition.");
+ tb.HasTrigger("PIMS_CMPRQP_A_S_IUD_TR");
+ tb.HasTrigger("PIMS_CMPRQP_I_S_I_TR");
+ tb.HasTrigger("PIMS_CMPRQP_I_S_U_TR");
+ });
+
+ entity.Property(e => e.CompReqPayeeId)
+ .HasDefaultValueSql("(NEXT VALUE FOR [PIMS_COMP_REQ_PAYEE_ID_SEQ])")
+ .HasComment("Generated surrogate primary key.");
+ entity.Property(e => e.AcquisitionFileTeamId).HasComment("Foreign key reference to the PIMS_ACQUISITION_FILE_TEAM table.");
+ entity.Property(e => e.AcquisitionOwnerId).HasComment("Foreign key reference to the PIMS_ACQUISITION_OWNER table.");
+ entity.Property(e => e.AppCreateTimestamp)
+ .HasDefaultValueSql("(getutcdate())")
+ .HasComment("The date and time the user created the record.");
+ entity.Property(e => e.AppCreateUserDirectory)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The directory of the user account that created the record.");
+ entity.Property(e => e.AppCreateUserGuid).HasComment("The GUID of the user account that created the record.");
+ entity.Property(e => e.AppCreateUserid)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The user account that created the record.");
+ entity.Property(e => e.AppLastUpdateTimestamp)
+ .HasDefaultValueSql("(getutcdate())")
+ .HasComment("The date and time the user updated the record.");
+ entity.Property(e => e.AppLastUpdateUserDirectory)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The directory of the user account that updated the record.");
+ entity.Property(e => e.AppLastUpdateUserGuid).HasComment("The GUID of the user account that updated the record.");
+ entity.Property(e => e.AppLastUpdateUserid)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The user account that updated the record.");
+ entity.Property(e => e.CompensationRequisitionId).HasComment("Foreign key reference to the PIMS_COMPENSATION_REQUISITION table.");
+ entity.Property(e => e.ConcurrencyControlNumber)
+ .HasDefaultValue(1L)
+ .HasComment("Application code is responsible for retrieving the row and then incrementing the value of the CONCURRENCY_CONTROL_NUMBER column by one prior to issuing an update. If this is done then the update will succeed, provided that the row was not updated by any o");
+ entity.Property(e => e.DbCreateTimestamp)
+ .HasDefaultValueSql("(getutcdate())")
+ .HasComment("The date and time the record was created.");
+ entity.Property(e => e.DbCreateUserid)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The user or proxy account that created the record.");
+ entity.Property(e => e.DbLastUpdateTimestamp)
+ .HasDefaultValueSql("(getutcdate())")
+ .HasComment("The date and time the record was created or last updated.");
+ entity.Property(e => e.DbLastUpdateUserid)
+ .HasDefaultValueSql("(user_name())")
+ .HasComment("The user or proxy account that created or last updated the record.");
+ entity.Property(e => e.InterestHolderId).HasComment("Foreign key reference to the PIMS_INTEREST_HOLDER table.");
+
+ entity.HasOne(d => d.AcquisitionFileTeam).WithMany(p => p.PimsCompReqPayees).HasConstraintName("PIM_ACQNTM_PIM_CMPRQP_FK");
+
+ entity.HasOne(d => d.AcquisitionOwner).WithMany(p => p.PimsCompReqPayees).HasConstraintName("PIM_ACQOWN_PIM_CMPRQP_FK");
+
+ entity.HasOne(d => d.CompensationRequisition).WithMany(p => p.PimsCompReqPayees).HasConstraintName("PIM_CMPREQ_PIM_CMPRQP_FK");
+
+ entity.HasOne(d => d.InterestHolder).WithMany(p => p.PimsCompReqPayees).HasConstraintName("PIM_INTHLD_PIM_CMPRQP_FK");
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.CompReqPayeeHistId).HasName("PIMS_CMPRQP_H_PK");
+
+ entity.Property(e => e.CompReqPayeeHistId).HasDefaultValueSql("(NEXT VALUE FOR [PIMS_COMP_REQ_PAYEE_H_ID_SEQ])");
+ entity.Property(e => e.EffectiveDateHist).HasDefaultValueSql("(getutcdate())");
+ });
+
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.CompensationRequisitionId).HasName("CMPREQ_PK");
@@ -2021,8 +2097,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasDefaultValueSql("(NEXT VALUE FOR [PIMS_COMPENSATION_REQUISITION_ID_SEQ])")
.HasComment("Generated surrogate primary key.");
entity.Property(e => e.AcquisitionFileId).HasComment("Foreign key to the PIMS_ACQUISITION_FILE table.");
- entity.Property(e => e.AcquisitionFileTeamId).HasComment("Foreign key to the PIMS_ACQUISITION_FILE_TEAM table.");
- entity.Property(e => e.AcquisitionOwnerId).HasComment("Foreign key to the PIMS_ACQUISITION_OWNER table.");
entity.Property(e => e.AgreementDt).HasComment("Agreement date.");
entity.Property(e => e.AlternateProjectId).HasComment("Link a file to an \"Alternate Project\", so the user can make alternate payments that may be due after the original file's project closes.");
entity.Property(e => e.AppCreateTimestamp)
@@ -2068,7 +2142,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.FiscalYear).HasComment("Fiscal year of the compensation requisition.");
entity.Property(e => e.GenerationDt).HasComment("Document generation date.");
entity.Property(e => e.GstNumber).HasComment("GST number of the organization receiving the payment.");
- entity.Property(e => e.InterestHolderId).HasComment("Foreign key to the PIMS_INTEREST_HOLDER table.");
entity.Property(e => e.IsDisabled)
.HasDefaultValue(false)
.HasComment("Indicates if the requisition is inactive.");
@@ -2086,16 +2159,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.HasOne(d => d.AcquisitionFile).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_ACQNFL_PIM_CMPREQ_FK");
- entity.HasOne(d => d.AcquisitionFileTeam).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_ACQPER_PIM_CMPREQ_FK");
-
- entity.HasOne(d => d.AcquisitionOwner).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_ACQOWN_PIM_CMPREQ_FK");
-
entity.HasOne(d => d.AlternateProject).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_PROJCT_PIM_CMPREQ_FK");
entity.HasOne(d => d.ChartOfAccounts).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_CHRTAC_PIM_CMPREQ_FK");
- entity.HasOne(d => d.InterestHolder).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_INTHLD_PIM_CMPREQ_FK");
-
entity.HasOne(d => d.Lease).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_LEASE_PIM_CMPREQ_FK");
entity.HasOne(d => d.Responsibility).WithMany(p => p.PimsCompensationRequisitions).HasConstraintName("PIM_RESPCD_PIM_CMPREQ_FK");
@@ -9332,6 +9399,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.HasSequence("PIMS_COMP_REQ_FINANCIAL_ID_SEQ")
.HasMin(1L)
.HasMax(2147483647L);
+ modelBuilder.HasSequence("PIMS_COMP_REQ_PAYEE_H_ID_SEQ")
+ .HasMin(1L)
+ .HasMax(2147483647L);
+ modelBuilder.HasSequence("PIMS_COMP_REQ_PAYEE_ID_SEQ")
+ .HasMin(1L)
+ .HasMax(2147483647L);
modelBuilder.HasSequence("PIMS_COMPENSATION_REQUISITION_H_ID_SEQ")
.HasMin(1L)
.HasMax(2147483647L);
@@ -9695,6 +9768,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.HasSequence("PIMS_PROJECT_PROPERTY_ID_SEQ")
.HasMin(1L)
.HasMax(2147483647L);
+ modelBuilder.HasSequence("PIMS_PROJECT_TEAM_ID_SEQ")
+ .HasMin(1L)
+ .HasMax(2147483647L);
modelBuilder.HasSequence("PIMS_PROJECT_WORKFLOW_MODEL_ID_SEQ")
.HasMin(1L)
.HasMax(2147483647L);
diff --git a/source/backend/entities/ef/PimsAcquisitionFileTeam.cs b/source/backend/entities/ef/PimsAcquisitionFileTeam.cs
index adff4ecab8..0cab12f8ef 100644
--- a/source/backend/entities/ef/PimsAcquisitionFileTeam.cs
+++ b/source/backend/entities/ef/PimsAcquisitionFileTeam.cs
@@ -115,7 +115,7 @@ public partial class PimsAcquisitionFileTeam
public virtual PimsPerson Person { get; set; }
[InverseProperty("AcquisitionFileTeam")]
- public virtual ICollection PimsCompensationRequisitions { get; set; } = new List();
+ public virtual ICollection PimsCompReqPayees { get; set; } = new List();
[ForeignKey("PrimaryContactId")]
[InverseProperty("PimsAcquisitionFileTeamPrimaryContacts")]
diff --git a/source/backend/entities/ef/PimsAcquisitionOwner.cs b/source/backend/entities/ef/PimsAcquisitionOwner.cs
index 169415fc4d..180b8e40ad 100644
--- a/source/backend/entities/ef/PimsAcquisitionOwner.cs
+++ b/source/backend/entities/ef/PimsAcquisitionOwner.cs
@@ -158,7 +158,7 @@ public partial class PimsAcquisitionOwner
public virtual PimsAddress Address { get; set; }
[InverseProperty("AcquisitionOwner")]
- public virtual ICollection PimsCompensationRequisitions { get; set; } = new List();
+ public virtual ICollection PimsCompReqPayees { get; set; } = new List();
[InverseProperty("AcquisitionOwner")]
public virtual ICollection PimsExpropriationPayments { get; set; } = new List();
diff --git a/source/backend/entities/ef/PimsCompReqPayee.cs b/source/backend/entities/ef/PimsCompReqPayee.cs
new file mode 100644
index 0000000000..7052392013
--- /dev/null
+++ b/source/backend/entities/ef/PimsCompReqPayee.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+
+namespace Pims.Dal.Entities;
+
+///
+/// Table to support multiple payees on a compensation requisition.
+///
+[Table("PIMS_COMP_REQ_PAYEE")]
+[Index("AcquisitionFileTeamId", Name = "CMPRQP_ACQUISITION_FILE_TEAM_ID_IDX")]
+[Index("CompensationRequisitionId", Name = "CMPRQP_COMPENSATION_REQUISITION_ID_IDX")]
+[Index("InterestHolderId", Name = "CMPRQP_INTEREST_HOLDER_ID_IDX")]
+public partial class PimsCompReqPayee
+{
+ ///
+ /// Generated surrogate primary key.
+ ///
+ [Key]
+ [Column("COMP_REQ_PAYEE_ID")]
+ public long CompReqPayeeId { get; set; }
+
+ ///
+ /// Foreign key reference to the PIMS_COMPENSATION_REQUISITION table.
+ ///
+ [Column("COMPENSATION_REQUISITION_ID")]
+ public long? CompensationRequisitionId { get; set; }
+
+ ///
+ /// Foreign key reference to the PIMS_ACQUISITION_OWNER table.
+ ///
+ [Column("ACQUISITION_OWNER_ID")]
+ public long? AcquisitionOwnerId { get; set; }
+
+ ///
+ /// Foreign key reference to the PIMS_INTEREST_HOLDER table.
+ ///
+ [Column("INTEREST_HOLDER_ID")]
+ public long? InterestHolderId { get; set; }
+
+ ///
+ /// Foreign key reference to the PIMS_ACQUISITION_FILE_TEAM table.
+ ///
+ [Column("ACQUISITION_FILE_TEAM_ID")]
+ public long? AcquisitionFileTeamId { get; set; }
+
+ ///
+ /// Application code is responsible for retrieving the row and then incrementing the value of the CONCURRENCY_CONTROL_NUMBER column by one prior to issuing an update. If this is done then the update will succeed, provided that the row was not updated by any o
+ ///
+ [Column("CONCURRENCY_CONTROL_NUMBER")]
+ public long ConcurrencyControlNumber { get; set; }
+
+ ///
+ /// The date and time the user created the record.
+ ///
+ [Column("APP_CREATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime AppCreateTimestamp { get; set; }
+
+ ///
+ /// The user account that created the record.
+ ///
+ [Required]
+ [Column("APP_CREATE_USERID")]
+ [StringLength(30)]
+ public string AppCreateUserid { get; set; }
+
+ ///
+ /// The GUID of the user account that created the record.
+ ///
+ [Column("APP_CREATE_USER_GUID")]
+ public Guid? AppCreateUserGuid { get; set; }
+
+ ///
+ /// The directory of the user account that created the record.
+ ///
+ [Required]
+ [Column("APP_CREATE_USER_DIRECTORY")]
+ [StringLength(30)]
+ public string AppCreateUserDirectory { get; set; }
+
+ ///
+ /// The date and time the user updated the record.
+ ///
+ [Column("APP_LAST_UPDATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime AppLastUpdateTimestamp { get; set; }
+
+ ///
+ /// The user account that updated the record.
+ ///
+ [Required]
+ [Column("APP_LAST_UPDATE_USERID")]
+ [StringLength(30)]
+ public string AppLastUpdateUserid { get; set; }
+
+ ///
+ /// The GUID of the user account that updated the record.
+ ///
+ [Column("APP_LAST_UPDATE_USER_GUID")]
+ public Guid? AppLastUpdateUserGuid { get; set; }
+
+ ///
+ /// The directory of the user account that updated the record.
+ ///
+ [Required]
+ [Column("APP_LAST_UPDATE_USER_DIRECTORY")]
+ [StringLength(30)]
+ public string AppLastUpdateUserDirectory { get; set; }
+
+ ///
+ /// The date and time the record was created.
+ ///
+ [Column("DB_CREATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime DbCreateTimestamp { get; set; }
+
+ ///
+ /// The user or proxy account that created the record.
+ ///
+ [Required]
+ [Column("DB_CREATE_USERID")]
+ [StringLength(30)]
+ public string DbCreateUserid { get; set; }
+
+ ///
+ /// The date and time the record was created or last updated.
+ ///
+ [Column("DB_LAST_UPDATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime DbLastUpdateTimestamp { get; set; }
+
+ ///
+ /// The user or proxy account that created or last updated the record.
+ ///
+ [Required]
+ [Column("DB_LAST_UPDATE_USERID")]
+ [StringLength(30)]
+ public string DbLastUpdateUserid { get; set; }
+
+ [ForeignKey("AcquisitionFileTeamId")]
+ [InverseProperty("PimsCompReqPayees")]
+ public virtual PimsAcquisitionFileTeam AcquisitionFileTeam { get; set; }
+
+ [ForeignKey("AcquisitionOwnerId")]
+ [InverseProperty("PimsCompReqPayees")]
+ public virtual PimsAcquisitionOwner AcquisitionOwner { get; set; }
+
+ [ForeignKey("CompensationRequisitionId")]
+ [InverseProperty("PimsCompReqPayees")]
+ public virtual PimsCompensationRequisition CompensationRequisition { get; set; }
+
+ [ForeignKey("InterestHolderId")]
+ [InverseProperty("PimsCompReqPayees")]
+ public virtual PimsInterestHolder InterestHolder { get; set; }
+}
diff --git a/source/backend/entities/ef/PimsCompReqPayeeHist.cs b/source/backend/entities/ef/PimsCompReqPayeeHist.cs
new file mode 100644
index 0000000000..e4fe656a7d
--- /dev/null
+++ b/source/backend/entities/ef/PimsCompReqPayeeHist.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+
+namespace Pims.Dal.Entities;
+
+[Table("PIMS_COMP_REQ_PAYEE_HIST")]
+[Index("CompReqPayeeHistId", "EndDateHist", Name = "PIMS_CMPRQP_H_UK", IsUnique = true)]
+public partial class PimsCompReqPayeeHist
+{
+ [Key]
+ [Column("_COMP_REQ_PAYEE_HIST_ID")]
+ public long CompReqPayeeHistId { get; set; }
+
+ [Column("EFFECTIVE_DATE_HIST", TypeName = "datetime")]
+ public DateTime EffectiveDateHist { get; set; }
+
+ [Column("END_DATE_HIST", TypeName = "datetime")]
+ public DateTime? EndDateHist { get; set; }
+
+ [Column("COMP_REQ_PAYEE_ID")]
+ public long CompReqPayeeId { get; set; }
+
+ [Column("COMPENSATION_REQUISITION_ID")]
+ public long? CompensationRequisitionId { get; set; }
+
+ [Column("ACQUISITION_OWNER_ID")]
+ public long? AcquisitionOwnerId { get; set; }
+
+ [Column("INTEREST_HOLDER_ID")]
+ public long? InterestHolderId { get; set; }
+
+ [Column("ACQUISITION_FILE_TEAM_ID")]
+ public long? AcquisitionFileTeamId { get; set; }
+
+ [Column("CONCURRENCY_CONTROL_NUMBER")]
+ public long ConcurrencyControlNumber { get; set; }
+
+ [Column("APP_CREATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime AppCreateTimestamp { get; set; }
+
+ [Required]
+ [Column("APP_CREATE_USERID")]
+ [StringLength(30)]
+ public string AppCreateUserid { get; set; }
+
+ [Column("APP_CREATE_USER_GUID")]
+ public Guid? AppCreateUserGuid { get; set; }
+
+ [Required]
+ [Column("APP_CREATE_USER_DIRECTORY")]
+ [StringLength(30)]
+ public string AppCreateUserDirectory { get; set; }
+
+ [Column("APP_LAST_UPDATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime AppLastUpdateTimestamp { get; set; }
+
+ [Required]
+ [Column("APP_LAST_UPDATE_USERID")]
+ [StringLength(30)]
+ public string AppLastUpdateUserid { get; set; }
+
+ [Column("APP_LAST_UPDATE_USER_GUID")]
+ public Guid? AppLastUpdateUserGuid { get; set; }
+
+ [Required]
+ [Column("APP_LAST_UPDATE_USER_DIRECTORY")]
+ [StringLength(30)]
+ public string AppLastUpdateUserDirectory { get; set; }
+
+ [Column("DB_CREATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime DbCreateTimestamp { get; set; }
+
+ [Required]
+ [Column("DB_CREATE_USERID")]
+ [StringLength(30)]
+ public string DbCreateUserid { get; set; }
+
+ [Column("DB_LAST_UPDATE_TIMESTAMP", TypeName = "datetime")]
+ public DateTime DbLastUpdateTimestamp { get; set; }
+
+ [Required]
+ [Column("DB_LAST_UPDATE_USERID")]
+ [StringLength(30)]
+ public string DbLastUpdateUserid { get; set; }
+}
diff --git a/source/backend/entities/ef/PimsCompensationRequisition.cs b/source/backend/entities/ef/PimsCompensationRequisition.cs
index 82b6d786c1..9464426b04 100644
--- a/source/backend/entities/ef/PimsCompensationRequisition.cs
+++ b/source/backend/entities/ef/PimsCompensationRequisition.cs
@@ -11,11 +11,8 @@ namespace Pims.Dal.Entities;
///
[Table("PIMS_COMPENSATION_REQUISITION")]
[Index("AcquisitionFileId", Name = "CMPREQ_ACQUISITION_FILE_ID_IDX")]
-[Index("AcquisitionFileTeamId", Name = "CMPREQ_ACQUISITION_FILE_PERSON_ID_IDX")]
-[Index("AcquisitionOwnerId", Name = "CMPREQ_ACQUISITION_OWNER_ID_IDX")]
[Index("AlternateProjectId", Name = "CMPREQ_ALTERNATE_PROJECT_ID_IDX")]
[Index("ChartOfAccountsId", Name = "CMPREQ_CHART_OF_ACCOUNTS_ID_IDX")]
-[Index("InterestHolderId", Name = "CMPREQ_INTEREST_HOLDER_ID_IDX")]
[Index("LeaseId", Name = "CMPREQ_LEASE_ID_IDX")]
[Index("ResponsibilityId", Name = "CMPREQ_RESPONSIBILITY_ID_IDX")]
[Index("YearlyFinancialId", Name = "CMPREQ_YEARLY_FINANCIAL_ID_IDX")]
@@ -40,24 +37,6 @@ public partial class PimsCompensationRequisition
[Column("LEASE_ID")]
public long? LeaseId { get; set; }
- ///
- /// Foreign key to the PIMS_ACQUISITION_OWNER table.
- ///
- [Column("ACQUISITION_OWNER_ID")]
- public long? AcquisitionOwnerId { get; set; }
-
- ///
- /// Foreign key to the PIMS_INTEREST_HOLDER table.
- ///
- [Column("INTEREST_HOLDER_ID")]
- public long? InterestHolderId { get; set; }
-
- ///
- /// Foreign key to the PIMS_ACQUISITION_FILE_TEAM table.
- ///
- [Column("ACQUISITION_FILE_TEAM_ID")]
- public long? AcquisitionFileTeamId { get; set; }
-
///
/// Foreign key to the PIMS_CHART_OF_ACCOUNTS table.
///
@@ -259,14 +238,6 @@ public partial class PimsCompensationRequisition
[InverseProperty("PimsCompensationRequisitions")]
public virtual PimsAcquisitionFile AcquisitionFile { get; set; }
- [ForeignKey("AcquisitionFileTeamId")]
- [InverseProperty("PimsCompensationRequisitions")]
- public virtual PimsAcquisitionFileTeam AcquisitionFileTeam { get; set; }
-
- [ForeignKey("AcquisitionOwnerId")]
- [InverseProperty("PimsCompensationRequisitions")]
- public virtual PimsAcquisitionOwner AcquisitionOwner { get; set; }
-
[ForeignKey("AlternateProjectId")]
[InverseProperty("PimsCompensationRequisitions")]
public virtual PimsProject AlternateProject { get; set; }
@@ -275,10 +246,6 @@ public partial class PimsCompensationRequisition
[InverseProperty("PimsCompensationRequisitions")]
public virtual PimsChartOfAccountsCode ChartOfAccounts { get; set; }
- [ForeignKey("InterestHolderId")]
- [InverseProperty("PimsCompensationRequisitions")]
- public virtual PimsInterestHolder InterestHolder { get; set; }
-
[ForeignKey("LeaseId")]
[InverseProperty("PimsCompensationRequisitions")]
public virtual PimsLease Lease { get; set; }
@@ -286,6 +253,9 @@ public partial class PimsCompensationRequisition
[InverseProperty("CompensationRequisition")]
public virtual ICollection PimsCompReqFinancials { get; set; } = new List();
+ [InverseProperty("CompensationRequisition")]
+ public virtual ICollection PimsCompReqPayees { get; set; } = new List();
+
[InverseProperty("CompensationRequisition")]
public virtual ICollection PimsLeaseStakeholderCompReqs { get; set; } = new List();
diff --git a/source/backend/entities/ef/PimsCompensationRequisitionHist.cs b/source/backend/entities/ef/PimsCompensationRequisitionHist.cs
index 062edaade3..9ed65f93d9 100644
--- a/source/backend/entities/ef/PimsCompensationRequisitionHist.cs
+++ b/source/backend/entities/ef/PimsCompensationRequisitionHist.cs
@@ -29,15 +29,6 @@ public partial class PimsCompensationRequisitionHist
[Column("LEASE_ID")]
public long? LeaseId { get; set; }
- [Column("ACQUISITION_OWNER_ID")]
- public long? AcquisitionOwnerId { get; set; }
-
- [Column("INTEREST_HOLDER_ID")]
- public long? InterestHolderId { get; set; }
-
- [Column("ACQUISITION_FILE_TEAM_ID")]
- public long? AcquisitionFileTeamId { get; set; }
-
[Column("CHART_OF_ACCOUNTS_ID")]
public long? ChartOfAccountsId { get; set; }
@@ -147,4 +138,13 @@ public partial class PimsCompensationRequisitionHist
[Column("ADV_PMT_SERVED_DT")]
public DateOnly? AdvPmtServedDt { get; set; }
+
+ [Column("ACQUISITION_OWNER_ID")]
+ public long? AcquisitionOwnerId { get; set; }
+
+ [Column("INTEREST_HOLDER_ID")]
+ public long? InterestHolderId { get; set; }
+
+ [Column("ACQUISITION_FILE_TEAM_ID")]
+ public long? AcquisitionFileTeamId { get; set; }
}
diff --git a/source/backend/entities/ef/PimsDocument.cs b/source/backend/entities/ef/PimsDocument.cs
index 1ebb797281..bf16d7a285 100644
--- a/source/backend/entities/ef/PimsDocument.cs
+++ b/source/backend/entities/ef/PimsDocument.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/source/backend/entities/ef/PimsDocumentQueue.cs b/source/backend/entities/ef/PimsDocumentQueue.cs
index 8efe09b4a7..a4dc9c20a1 100644
--- a/source/backend/entities/ef/PimsDocumentQueue.cs
+++ b/source/backend/entities/ef/PimsDocumentQueue.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -29,7 +29,7 @@ public partial class PimsDocumentQueue
public long? DocumentId { get; set; }
///
- /// Code value that represents the current status of the document as it is processed by PIMS/MAYAN.
+ /// Code value that represents the current status of the document as it is processed by PIMS/MAYAN
///
[Required]
[Column("DOCUMENT_QUEUE_STATUS_TYPE_CODE")]
@@ -65,7 +65,7 @@ public partial class PimsDocumentQueue
public DateTime? DocProcessStartDt { get; set; }
///
- /// When the document?s processing finishes, this will be populated.
+ /// When the document?s processing finishes, this will be populated
///
[Column("DOC_PROCESS_END_DT", TypeName = "datetime")]
public DateTime? DocProcessEndDt { get; set; }
@@ -90,7 +90,7 @@ public partial class PimsDocumentQueue
public byte[] Document { get; set; }
///
- /// Application code is responsible for retrieving the row and then incrementing the value of the CONCURRENCY_CONTROL_NUMBER column by one prior to issuing an update. If this is done then the update will succeed, provided that the row was not updated by any o.
+ /// Application code is responsible for retrieving the row and then incrementing the value of the CONCURRENCY_CONTROL_NUMBER column by one prior to issuing an update. If this is done then the update will succeed, provided that the row was not updated by any o
///
[Column("CONCURRENCY_CONTROL_NUMBER")]
public long ConcurrencyControlNumber { get; set; }
diff --git a/source/backend/entities/ef/PimsInterestHolder.cs b/source/backend/entities/ef/PimsInterestHolder.cs
index db9115d24a..1c20e3e5b2 100644
--- a/source/backend/entities/ef/PimsInterestHolder.cs
+++ b/source/backend/entities/ef/PimsInterestHolder.cs
@@ -122,7 +122,7 @@ public partial class PimsInterestHolder
public virtual PimsPerson Person { get; set; }
[InverseProperty("InterestHolder")]
- public virtual ICollection PimsCompensationRequisitions { get; set; } = new List();
+ public virtual ICollection PimsCompReqPayees { get; set; } = new List();
[InverseProperty("InterestHolder")]
public virtual ICollection PimsExpropriationPayments { get; set; } = new List();
diff --git a/source/backend/entities/ef/PimsLease.cs b/source/backend/entities/ef/PimsLease.cs
index 275b4404d4..d4dcfdd61f 100644
--- a/source/backend/entities/ef/PimsLease.cs
+++ b/source/backend/entities/ef/PimsLease.cs
@@ -127,7 +127,7 @@ public partial class PimsLease
/// Manually etered lease description, not the legal description
///
[Column("LEASE_DESCRIPTION")]
- [StringLength(2000)]
+ [StringLength(4000)]
public string LeaseDescription { get; set; }
///
diff --git a/source/backend/entities/ef/PimsLeaseHist.cs b/source/backend/entities/ef/PimsLeaseHist.cs
index 4c1fadd221..484cc820fc 100644
--- a/source/backend/entities/ef/PimsLeaseHist.cs
+++ b/source/backend/entities/ef/PimsLeaseHist.cs
@@ -76,7 +76,7 @@ public partial class PimsLeaseHist
public string PsFileNo { get; set; }
[Column("LEASE_DESCRIPTION")]
- [StringLength(2000)]
+ [StringLength(4000)]
public string LeaseDescription { get; set; }
[Column("LEASE_NOTES")]
diff --git a/source/backend/entities/ef/PimsPropertyResearchFile.cs b/source/backend/entities/ef/PimsPropertyResearchFile.cs
index 99463e5206..a8e6fab703 100644
--- a/source/backend/entities/ef/PimsPropertyResearchFile.cs
+++ b/source/backend/entities/ef/PimsPropertyResearchFile.cs
@@ -62,7 +62,7 @@ public partial class PimsPropertyResearchFile
/// Summary of the property research.
///
[Column("RESEARCH_SUMMARY")]
- [StringLength(1000)]
+ [StringLength(4000)]
public string ResearchSummary { get; set; }
///
diff --git a/source/backend/entities/ef/PimsPropertyResearchFileHist.cs b/source/backend/entities/ef/PimsPropertyResearchFileHist.cs
index 6fbae6bd0e..19dbfcc3ed 100644
--- a/source/backend/entities/ef/PimsPropertyResearchFileHist.cs
+++ b/source/backend/entities/ef/PimsPropertyResearchFileHist.cs
@@ -47,7 +47,7 @@ public partial class PimsPropertyResearchFileHist
public string DocumentReference { get; set; }
[Column("RESEARCH_SUMMARY")]
- [StringLength(1000)]
+ [StringLength(4000)]
public string ResearchSummary { get; set; }
[Column("CONCURRENCY_CONTROL_NUMBER")]
diff --git a/source/backend/scheduler/appsettings.json b/source/backend/scheduler/appsettings.json
index b86e27e089..a2db478a12 100644
--- a/source/backend/scheduler/appsettings.json
+++ b/source/backend/scheduler/appsettings.json
@@ -47,10 +47,10 @@
},
"Server": {
"HeartbeatInterval": "00:00:30",
- "Queues": ["default"],
+ "Queues": ["scheduler"],
"SchedulePollingInterval": "00:00:15",
"ServerCheckInterval": "00:05:00",
- "ServerName": null,
+ "ServerName": "scheduler",
"ServerTimeout": "00:05:00",
"ShutdownTimeout": "00:00:15",
"WorkerCount": 20
diff --git a/source/backend/tests/unit/api/Helpers/PrincipalExtensionsTest.cs b/source/backend/tests/unit/api/Helpers/PrincipalExtensionsTest.cs
index f7e9fc91f3..09c138fea8 100644
--- a/source/backend/tests/unit/api/Helpers/PrincipalExtensionsTest.cs
+++ b/source/backend/tests/unit/api/Helpers/PrincipalExtensionsTest.cs
@@ -1,10 +1,18 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Security.Claims;
+using DocumentFormat.OpenXml.Office2010.ExcelAc;
using FluentAssertions;
+using k8s.KubeConfigModels;
+using Moq;
+using Pims.Api.Services;
using Pims.Core.Exceptions;
+using Pims.Core.Extensions;
using Pims.Core.Test;
using Pims.Dal.Entities;
using Pims.Dal.Exceptions;
+using Pims.Dal.Repositories;
using Xunit;
namespace Pims.Api.Test.Helpers
@@ -46,6 +54,146 @@ public void LeaseRegionUserAccess_HasRegion_Success()
Action act = () => pimsUser.ThrowInvalidAccessToLeaseFile(1);
act.Should().NotThrow();
}
+
+ #region Tests
+ [Fact]
+ public void ThrowInvalidAccessToAcquisitionFile_ContractorNotAssigned_ThrowsNotAuthorizedException()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var acquisitionFileRepository = _helper.GetService>();
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ var acquisitionFile = EntityHelper.CreateAcquisitionFile(1);
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ acquisitionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(acquisitionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToAcquisitionFile(userRepository.Object, acquisitionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().Throw().WithMessage("Contractor is not assigned to the Acquisition File's team");
+ }
+
+ [Fact]
+ public void ThrowInvalidAccessToAcquisitionFile_ContractorAssigned_DoesNotThrow()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var acquisitionFileRepository = _helper.GetService>();
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ var acquisitionFile = EntityHelper.CreateAcquisitionFile(1);
+ acquisitionFile.PimsAcquisitionFileTeams.Add(new PimsAcquisitionFileTeam { PersonId = pimsUser.PersonId });
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ acquisitionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(acquisitionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToAcquisitionFile(userRepository.Object, acquisitionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().NotThrow();
+ }
+
+ [Fact]
+ public void ThrowInvalidAccessToAcquisitionFile_ContractorProject_DoesNotThrow()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var acquisitionFileRepository = _helper.GetService>();
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ pimsUser.PersonId = 1;
+ var acquisitionFile = EntityHelper.CreateAcquisitionFile(1);
+ acquisitionFile.Project = new PimsProject() { PimsProjectPeople = new List() { new PimsProjectPerson() { PersonId = 1 } } };
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ acquisitionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(acquisitionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToAcquisitionFile(userRepository.Object, acquisitionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().NotThrow();
+ }
+
+ [Fact]
+ public void ThrowInvalidAccessToDispositionFile_ContractorNotAssigned_ThrowsNotAuthorizedException()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var dispositionFileRepository = _helper.GetService>();
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ var dispositionFile = EntityHelper.CreateDispositionFile(1);
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ dispositionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(dispositionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToDispositionFile(userRepository.Object, dispositionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().Throw().WithMessage("Contractor is not assigned to the Disposition File's team");
+ }
+
+ [Fact]
+ public void ThrowInvalidAccessToDispositionFile_ContractorAssigned_DoesNotThrow()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var dispositionFileRepository = _helper.GetService>();
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ var dispositionFile = EntityHelper.CreateDispositionFile(1);
+ dispositionFile.PimsDispositionFileTeams.Add(new PimsDispositionFileTeam { PersonId = pimsUser.PersonId });
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ dispositionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(dispositionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToDispositionFile(userRepository.Object, dispositionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().NotThrow();
+ }
+
+ [Fact]
+ public void ThrowInvalidAccessToDispositionFile_ContractorProject_DoesThrow()
+ {
+ // Arrange
+ this._helper.Create();
+
+ var principal = new ClaimsPrincipal();
+ var userRepository = _helper.GetService>();
+ var dispositionFileRepository = _helper.GetService>();
+ var dispositionFile = EntityHelper.CreateDispositionFile(1);
+ var pimsUser = EntityHelper.CreateUser(1, Guid.NewGuid(), "testuser", regionCode: 1, isContractor: true);
+ pimsUser.PersonId = 1;
+ dispositionFile.Project = new PimsProject() { PimsProjectPeople = new List() { new PimsProjectPerson() { PersonId = 1 } } };
+
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(pimsUser);
+ dispositionFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(dispositionFile);
+
+ // Act
+ Action act = () => principal.ThrowInvalidAccessToDispositionFile(userRepository.Object, dispositionFileRepository.Object, 1);
+
+ // Assert
+ act.Should().Throw();
+ }
+ #endregion
#endregion
}
}
diff --git a/source/backend/tests/unit/api/Repositories/BaseRestRepositoryTest.cs b/source/backend/tests/unit/api/Repositories/BaseRestRepositoryTest.cs
new file mode 100644
index 0000000000..62feec20d2
--- /dev/null
+++ b/source/backend/tests/unit/api/Repositories/BaseRestRepositoryTest.cs
@@ -0,0 +1,402 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Moq;
+using Pims.Api.Models;
+using Pims.Api.Models.CodeTypes;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Requests.Http;
+using Pims.Core.Api.Repositories.Rest;
+using Pims.Core.Extensions;
+using Pims.Core.Test.Http;
+using Xunit;
+
+namespace Pims.Core.Test.Repositories.Rest
+{
+ public class BaseRestRepositoryTest
+ {
+ private readonly Mock> _logger;
+ private readonly Mock _httpClientFactory;
+ private readonly Mock> _jsonOptions;
+ private readonly TestBaseRestRepository _repository;
+
+ public BaseRestRepositoryTest()
+ {
+ _logger = new Mock>();
+ _httpClientFactory = new Mock();
+ _jsonOptions = new Mock>();
+ _repository = new TestBaseRestRepository(
+ _logger.Object,
+ _httpClientFactory.Object,
+ _jsonOptions.Object);
+ }
+
+ [Fact]
+ public async Task GetAsync_Success()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Payload = new DocumentTypeModel(), HttpStatusCode = HttpStatusCode.OK };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(new DocumentTypeModel().Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task GetAsync_NoContent()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Message = "No content was returned from the call", HttpStatusCode = HttpStatusCode.NoContent };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task GetRawAsync_Success()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Test") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetRawAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.StatusCode.Should().Be(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task GetRawAsync_NoContent()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetRawAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.StatusCode.Should().Be(HttpStatusCode.NoContent);
+ }
+
+ [Fact]
+ public async Task GetRawAsync_Unauthorized()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Unauthorized)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetRawAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
+ }
+
+ [Fact]
+ public async Task GetRawAsync_InternalServerError()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.InternalServerError)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetRawAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
+ }
+
+ [Fact]
+ public async Task PostAsync_Success()
+ {
+ var documentDetail = new DocumentDetailModel { Id = 1, Label = "Test" };
+
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Payload = documentDetail, HttpStatusCode = HttpStatusCode.OK };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentDetail.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PostAsync_BadRequest()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Bad Request", HttpStatusCode = HttpStatusCode.BadRequest };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("Bad Request") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PostAsync_Unauthorized()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Request was forbidden", HttpStatusCode = HttpStatusCode.Forbidden };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("Unauthorized") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PostAsync_InternalServerError()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Unable to contact endpoint . Http status InternalServerError", HttpStatusCode = HttpStatusCode.InternalServerError };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent("Internal Server Error") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PostAsync_Exception()
+ {
+ Mock httpClient = new Mock();
+
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Exception during Post" };
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient.Object);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PostAsync_NotFound()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "The requested resource does not exist on the server", HttpStatusCode = HttpStatusCode.NotFound };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NotFound) { Content = new StringContent("Not Found") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PostAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PutAsync_Success()
+ {
+ var documentDetail = new DocumentDetailModel { Id = 1, Label = "Test" };
+
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Payload = documentDetail, HttpStatusCode = HttpStatusCode.OK };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentDetail.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PutAsync(new Uri("http://test.com"), new StringContent(documentDetail.Serialize()), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PutAsync_BadRequest()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Bad Request", HttpStatusCode = HttpStatusCode.BadRequest };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("Bad Request") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PutAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PutAsync_Unauthorized()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Request was forbidden", HttpStatusCode = HttpStatusCode.Forbidden };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("Unauthorized") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PutAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PutAsync_InternalServerError()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Unable to contact endpoint . Http status InternalServerError", HttpStatusCode = HttpStatusCode.InternalServerError };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent("Internal Server Error") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.PutAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task PutAsync_Exception()
+ {
+ Mock httpClient = new Mock();
+
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Exception during Put" };
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient.Object);
+
+ // Act
+ var result = await _repository.PutAsync(new Uri("http://test.com"), new StringContent("Test"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_Success()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Payload = "/", HttpStatusCode = HttpStatusCode.OK };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Test") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_NoContent()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Success, Message = "No content was returned from the call", Payload = "/", HttpStatusCode = HttpStatusCode.NoContent };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_Unauthorized()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Request was forbidden", Payload = "/", HttpStatusCode = HttpStatusCode.Forbidden };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("Request was forbidden") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_BadRequest()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Message = "Internal Server Error", Payload = "/", HttpStatusCode = HttpStatusCode.BadRequest };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("Internal Server Error") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_InternalServerError()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Payload = "/", Message = "Unable to contact endpoint . Http status InternalServerError", HttpStatusCode = HttpStatusCode.InternalServerError };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent("Internal Server Error") }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_Exception()
+ {
+ // Arrange
+ var expectedResponse = new ExternalResponse { Status = ExternalResponseStatus.Error, Payload = "/", Message = "Exception during Delete" };
+ Mock httpClient = new Mock();
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient.Object);
+
+ // Act
+ var result = await _repository.DeleteAsync(new Uri("http://test.com"), "token");
+
+ // Assert
+ result.Should().BeEquivalentTo(expectedResponse);
+ }
+
+ // Helper class to test the abstract BaseRestRepository
+ private class TestBaseRestRepository : BaseRestRepository
+ {
+ public TestBaseRestRepository(ILogger logger, IHttpClientFactory httpClientFactory, IOptions jsonOptions)
+ : base(logger, httpClientFactory, jsonOptions)
+ {
+ }
+
+ public override void AddAuthentication(HttpClient client, string authenticationToken = null)
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationToken);
+ }
+ }
+ }
+}
diff --git a/source/backend/tests/unit/api/Repositories/MayanAuthRepositoryTest.cs b/source/backend/tests/unit/api/Repositories/MayanAuthRepositoryTest.cs
new file mode 100644
index 0000000000..a5c7ef1936
--- /dev/null
+++ b/source/backend/tests/unit/api/Repositories/MayanAuthRepositoryTest.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Moq;
+using Moq.Protected;
+using Pims.Api.Models.CodeTypes;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Requests.Http;
+using Pims.Api.Repositories.Mayan;
+using Pims.Core.Api.Exceptions;
+using Xunit;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ public class MayanAuthRepositoryTest
+ {
+ private readonly Mock> _logger;
+ private readonly Mock _httpClientFactory;
+ private readonly IOptions _jsonOptions;
+ private readonly IConfiguration _configuration;
+ private readonly MayanAuthRepository _repository;
+
+ public MayanAuthRepositoryTest()
+ {
+ _logger = new Mock>();
+ _httpClientFactory = new Mock();
+ _jsonOptions = Options.Create(new JsonSerializerOptions() { AllowTrailingCommas = true, PropertyNameCaseInsensitive = true });
+ _configuration = _configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { { "Mayan:BaseUri", "http://mayan" } }).Build();
+ _repository = new MayanAuthRepository(
+ _logger.Object,
+ _httpClientFactory.Object,
+ _configuration,
+ _jsonOptions);
+ }
+
+ [Fact]
+ public async Task GetTokenAsync_Success()
+ {
+ // Arrange
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new TokenResponse
+ { Token = "test-token" }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.GetTokenAsync();
+
+ // Assert
+ result.Should().Be("test-token");
+ }
+
+ [Fact]
+ public async Task GetTokenAsync_Failure()
+ {
+ // Arrange
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.Forbidden)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new TokenResponse
+ { Token = "test-token" }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ Func act = async () => await _repository.GetTokenAsync();
+
+ // Assert
+ await act.Should().ThrowAsync().WithMessage("Request was forbidden");
+ }
+ }
+}
diff --git a/source/backend/tests/unit/api/Repositories/MayanDocumentRepositoryTest.cs b/source/backend/tests/unit/api/Repositories/MayanDocumentRepositoryTest.cs
new file mode 100644
index 0000000000..3b0757ba01
--- /dev/null
+++ b/source/backend/tests/unit/api/Repositories/MayanDocumentRepositoryTest.cs
@@ -0,0 +1,597 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Wordprocessing;
+using FluentAssertions;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Moq;
+using Moq.Protected;
+using Pims.Api.Models;
+using Pims.Api.Models.CodeTypes;
+using Pims.Api.Models.Config;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Mayan.Metadata;
+using Pims.Api.Repositories.Mayan;
+using Pims.Core.Extensions;
+using Pims.Core.Test.Http;
+using Xunit;
+using DocumentTypeModel = Pims.Api.Models.Mayan.Document.DocumentTypeModel;
+
+namespace Pims.Api.Test.Repositories
+{
+ public class MayanDocumentRepositoryTest
+ {
+ private readonly Mock> _logger;
+ private readonly Mock _httpClientFactory;
+ private readonly Mock _authRepository;
+ private readonly Mock> _jsonOptions;
+ private readonly IConfiguration _configuration;
+ private readonly MayanDocumentRepository _repository;
+
+ public MayanDocumentRepositoryTest()
+ {
+ _logger = new Mock>();
+ _httpClientFactory = new Mock();
+ _authRepository = new Mock();
+ _jsonOptions = new Mock>();
+ _configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { { "Mayan:BaseUri", "http://mayan" } }).Build();
+
+ _repository = new MayanDocumentRepository(
+ _logger.Object,
+ _httpClientFactory.Object,
+ _authRepository.Object,
+ _configuration,
+ _jsonOptions.Object);
+ }
+
+ [Fact]
+ public async Task TryCreateDocumentTypeAsync_Success()
+ {
+ // Arrange
+ var documentType = new DocumentTypeModel { Id = 1, Label = "Test" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentType.Serialize()) { } }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryCreateDocumentTypeAsync(documentType);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentType);
+ }
+
+ [Fact]
+ public async Task TryUpdateDocumentTypeAsync_Success()
+ {
+ // Arrange
+ var documentType = new DocumentTypeModel { Id = 1, Label = "Test" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentType.Serialize()) { } }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryUpdateDocumentTypeAsync(documentType);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentType);
+ }
+
+ [Fact]
+ public async Task TryUpdateDocumentTypeAsync_Failure()
+ {
+ // Arrange
+ var documentId = 1L;
+ var documentTypeId = 2L;
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest)
+ {
+ Content = new StringContent("{\"error\": \"bad request\"}")
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+
+ // Act
+ var result = await _repository.TryUpdateDocumentTypeAsync(documentId, documentTypeId);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest);
+ result.Payload.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task TryDeleteDocumentTypeAsync_Success()
+ {
+ // Arrange
+ var documentTypeId = 1;
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDeleteDocumentTypeAsync(documentTypeId);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryGetDocumentTypesAsync_Success()
+ {
+ // Arrange
+ var documentTypes = new QueryResponse
+ {
+ Results = new List { new DocumentTypeModel { Id = 1, Label = "Test" } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentTypes.Serialize()) { } }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetDocumentTypesAsync();
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryGetDocumentTypeMetadataTypesAsync_Success()
+ {
+ // Arrange
+ var documentTypeId = 1;
+ var metadataTypes = new QueryResponse
+ {
+ Results = new List { new DocumentTypeMetadataTypeModel { Id = 1, DocumentType = new DocumentTypeModel() } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(metadataTypes.Serialize()) { } }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetDocumentTypeMetadataTypesAsync(documentTypeId);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryGetDocumentsListAsync_Success()
+ {
+ // Arrange
+ var documents = new QueryResponse
+ {
+ Results = new List { new DocumentDetailModel { Id = 1 } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documents.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetDocumentsListAsync();
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryGetDocumentAsync_Success()
+ {
+ // Arrange
+ var document = new DocumentDetailModel { Id = 1 };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(document.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetDocumentAsync(1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(document);
+ }
+
+ [Fact]
+ public async Task TryGetDocumentMetadataAsync_Success()
+ {
+ // Arrange
+ var metadata = new QueryResponse
+ {
+ Results = new List { new DocumentMetadataModel { Id = 1, Value = "Test" } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(metadata.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetDocumentMetadataAsync(1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryDownloadFileAsync_Success()
+ {
+ // Arrange
+ var fileStreamResponse = new FileStreamResponse { FilePayload = new MemoryStream(), Mimetype = "application/pdf" };
+ var fileContent = "Test file content";
+ var content = new StringContent(fileContent);
+ content.Headers.ContentType = new MediaTypeHeaderValue(fileStreamResponse.Mimetype);
+ content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Test.pdf" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = content }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDownloadFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.FileName.Should().Be("Test.pdf");
+ result.Payload.FilePayload.Should().Be("VGVzdCBmaWxlIGNvbnRlbnQ=");
+ }
+
+ [Fact]
+ public async Task TryDownloadFileAsync_NoContent()
+ {
+ // Arrange
+ var fileStreamResponse = new FileStreamResponse { FilePayload = new MemoryStream(), Mimetype = "application/pdf" };
+ var fileContent = "Test file content";
+ var content = new StringContent(fileContent);
+ content.Headers.ContentType = new MediaTypeHeaderValue(fileStreamResponse.Mimetype);
+ content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Test.pdf" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent) { Content = content }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDownloadFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Message.Should().Be("No content found");
+ result.Payload.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task TryDownloadFileAsync_Forbidden()
+ {
+ // Arrange
+ var fileStreamResponse = new FileStreamResponse { FilePayload = new MemoryStream(), Mimetype = "application/pdf" };
+ var fileContent = "Test file content";
+ var content = new StringContent(fileContent);
+ content.Headers.ContentType = new MediaTypeHeaderValue(fileStreamResponse.Mimetype);
+ content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Test.pdf" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = content }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDownloadFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Error);
+ result.Message.Should().Be("Forbidden");
+ }
+
+ [Fact]
+ public async Task TryDownloadFileAsync_InternalServerError()
+ {
+ // Arrange
+ var fileStreamResponse = new FileStreamResponse { FilePayload = new MemoryStream(), Mimetype = "application/pdf" };
+ var fileContent = "Test file content";
+ var content = new StringContent(fileContent);
+ content.Headers.ContentType = new MediaTypeHeaderValue(fileStreamResponse.Mimetype);
+ content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Test.pdf" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = content }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDownloadFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Error);
+ result.Message.Should().Be("Unable to contact endpoint . Http status InternalServerError");
+ }
+
+ [Fact]
+ public async Task TryDownloadFileAsync_Exception()
+ {
+ // Arrange
+ var httpClient = new Mock();
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient.Object);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDownloadFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Error);
+ result.Message.Should().Be("Exception downloading file");
+ }
+
+ [Fact]
+ public async Task TryStreamFileAsync_Success()
+ {
+ // Arrange
+ var fileStreamResponse = new FileStreamResponse { FilePayload = new MemoryStream(), Mimetype = "application/pdf" };
+ var content = new StreamContent(fileStreamResponse.FilePayload);
+ content.Headers.ContentType = new MediaTypeHeaderValue(fileStreamResponse.Mimetype);
+ content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Test.pdf" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = content, }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryStreamFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task TryStreamFileAsync_NoResult()
+ {
+ // Arrange
+ var content = new StreamContent(new MemoryStream());
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent) { Content = content, }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryStreamFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Message.Should().Be("No content found");
+ }
+
+ [Fact]
+ public async Task TryStreamFileAsync_Forbidden()
+ {
+ // Arrange
+ var content = new StreamContent(new MemoryStream());
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = content, }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryStreamFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Error);
+ result.Message.Should().Be("Forbidden");
+ }
+
+ [Fact]
+ public async Task TryStreamFileAsync_OtherError()
+ {
+ // Arrange
+ var content = new StreamContent(new MemoryStream());
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NotFound) { Content = content, }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryStreamFileAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Error);
+ result.Message.Should().Be("Exception downloading file");
+ }
+
+ [Fact]
+ public async Task TryDeleteDocument_Success()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDeleteDocument(1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryUploadDocumentAsync_Success()
+ {
+ // Arrange
+ var documentDetail = new DocumentDetailModel { Id = 1, Label = "Test" };
+ using MemoryStream memStream = new MemoryStream();
+ var file = new FormFile(memStream, 0, memStream.Length, "test", "test");
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Created) { Content = new StringContent(documentDetail.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryUploadDocumentAsync(1, file);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentDetail);
+ }
+
+ [Fact]
+ public async Task TryGetMetadataTypesAsync_Success()
+ {
+ // Arrange
+ var metadataTypes = new QueryResponse
+ {
+ Results = new List { new MetadataTypeModel { Id = 1, Label = "Test" } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(metadataTypes.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetMetadataTypesAsync();
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryCreateDocumentTypeMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var documentTypeMetadataType = new DocumentTypeMetadataTypeModel { Id = 1, DocumentType = new DocumentTypeModel() };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Created) { Content = new StringContent(documentTypeMetadataType.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryCreateDocumentTypeMetadataTypeAsync(1, 1, true);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentTypeMetadataType);
+ }
+
+ [Fact]
+ public async Task TryCreateDocumentMetadataAsync_Success()
+ {
+ // Arrange
+ var documentMetadata = new DocumentMetadataModel { Id = 1, Value = "Test" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.Created) { Content = new StringContent(documentMetadata.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryCreateDocumentMetadataAsync(1, 1, "Test");
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentMetadata);
+ }
+
+ [Fact]
+ public async Task TryUpdateDocumentMetadataAsync_Success()
+ {
+ // Arrange
+ var documentMetadata = new DocumentMetadataModel { Id = 1, Value = "Updated" };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentMetadata.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryUpdateDocumentMetadataAsync(1, 1, "Updated");
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentMetadata);
+ }
+
+ [Fact]
+ public async Task TryDeleteDocumentMetadataAsync_Success()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDeleteDocumentMetadataAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryUpdateDocumentTypeMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var documentTypeMetadataType = new DocumentTypeMetadataTypeModel { Id = 1, DocumentType = new DocumentTypeModel() };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(documentTypeMetadataType.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryUpdateDocumentTypeMetadataTypeAsync(1, 1, true);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Should().BeEquivalentTo(documentTypeMetadataType);
+ }
+
+ [Fact]
+ public async Task TryDeleteDocumentTypeMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.NoContent)));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryDeleteDocumentTypeMetadataTypeAsync(1, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryGetFilePageListAsync_Success()
+ {
+ // Arrange
+ var filePages = new QueryResponse
+ {
+ Results = new List { new FilePageModel { Id = 1, PageNumber = 1 } }
+ };
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(filePages.Serialize()) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetFilePageListAsync(1, 1, 10, 1);
+
+ // Assert
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ result.Payload.Results.Should().HaveCount(1);
+ }
+
+ [Fact]
+ public async Task TryGetFilePageImage_Success()
+ {
+ // Arrange
+ var httpClient = new HttpClient(new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(new byte[] { 1, 2, 3 }) }));
+ _httpClientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(x => x.GetTokenAsync()).ReturnsAsync("token");
+
+ // Act
+ var result = await _repository.TryGetFilePageImage(1, 1, 1);
+
+ // Assert
+ result.StatusCode.Should().Be(HttpStatusCode.OK);
+ }
+ }
+}
diff --git a/source/backend/tests/unit/api/Repositories/MayanMetadataRepositoryTest.cs b/source/backend/tests/unit/api/Repositories/MayanMetadataRepositoryTest.cs
new file mode 100644
index 0000000000..40eb4239a9
--- /dev/null
+++ b/source/backend/tests/unit/api/Repositories/MayanMetadataRepositoryTest.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Pims.Api.Test.Repositories
+{
+ using System.Net;
+ using System.Net.Http;
+ using System.Text.Json;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using FluentAssertions;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Logging;
+ using Microsoft.Extensions.Options;
+ using Moq;
+ using Moq.Protected;
+ using Pims.Api.Models.CodeTypes;
+ using Pims.Api.Models.Mayan;
+ using Pims.Api.Models.Mayan.Metadata;
+ using Pims.Api.Models.Requests.Http;
+ using Pims.Api.Repositories.Mayan;
+ using Xunit;
+
+ public class MayanMetadataRepositoryTest
+ {
+ private readonly Mock> _logger;
+ private readonly Mock _httpClientFactory;
+ private readonly Mock _authRepository;
+ private readonly Mock> _jsonOptions;
+ private readonly IConfiguration _configuration;
+ private readonly MayanMetadataRepository _repository;
+
+ public MayanMetadataRepositoryTest()
+ {
+ _logger = new Mock>();
+ _httpClientFactory = new Mock();
+ _authRepository = new Mock();
+ _jsonOptions = new Mock>();
+ _configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { { "Mayan:BaseUri", "http://mayan" } }).Build();
+ _repository = new MayanMetadataRepository(
+ _logger.Object,
+ _httpClientFactory.Object,
+ _authRepository.Object,
+ _configuration,
+ _jsonOptions.Object);
+ }
+
+ [Fact]
+ public async Task TryGetMetadataTypesAsync_Success()
+ {
+ // Arrange
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new ExternalResponse>
+ {
+ Status = ExternalResponseStatus.Success,
+ Payload = new QueryResponse()
+ }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(_ => _.GetTokenAsync()).ReturnsAsync("test-token");
+
+ // Act
+ var result = await _repository.TryGetMetadataTypesAsync();
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryCreateMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var metadataType = new MetadataTypeModel();
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new ExternalResponse
+ {
+ Status = ExternalResponseStatus.Success,
+ Payload = metadataType
+ }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(_ => _.GetTokenAsync()).ReturnsAsync("test-token");
+
+ // Act
+ var result = await _repository.TryCreateMetadataTypeAsync(metadataType);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryUpdateMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var metadataType = new MetadataTypeModel { Id = 1 };
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new ExternalResponse
+ {
+ Status = ExternalResponseStatus.Success,
+ Payload = metadataType
+ }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(_ => _.GetTokenAsync()).ReturnsAsync("test-token");
+
+ // Act
+ var result = await _repository.TryUpdateMetadataTypeAsync(metadataType);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task TryDeleteMetadataTypeAsync_Success()
+ {
+ // Arrange
+ var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(new ExternalResponse
+ {
+ Status = ExternalResponseStatus.Success,
+ Payload = "Deleted"
+ }))
+ };
+
+ var httpMessageHandler = new Mock();
+ httpMessageHandler.Protected()
+ .Setup>(
+ "SendAsync",
+ ItExpr.IsAny(),
+ ItExpr.IsAny())
+ .ReturnsAsync(responseMessage);
+
+ var httpClient = new HttpClient(httpMessageHandler.Object);
+ _httpClientFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(httpClient);
+ _authRepository.Setup(_ => _.GetTokenAsync()).ReturnsAsync("test-token");
+
+ // Act
+ var result = await _repository.TryDeleteMetadataTypeAsync(1);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Status.Should().Be(ExternalResponseStatus.Success);
+ }
+ }
+}
diff --git a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs
index 4626d5cfc7..f08af88aaa 100644
--- a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs
+++ b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs
@@ -210,6 +210,38 @@ public void Add_Success_IsContractor_AssignedToTeam()
repository.Verify(x => x.Add(It.IsAny()), Times.Once);
}
+ [Fact]
+ public void Add_Success_IsContractor_AssignedToProject()
+ {
+ // Arrange
+ var service = this.CreateAcquisitionServiceWithPermissions(Permissions.AcquisitionFileAdd);
+
+ var acqFile = EntityHelper.CreateAcquisitionFile();
+ acqFile.Project = new PimsProject() { Id = 1, PimsProjectPeople = new List() { new PimsProjectPerson() { PersonId = 1, ProjectId = 1 } } };
+ acqFile.ProjectId = 1;
+ acqFile.ConcurrencyControlNumber = 1;
+
+ var userRepository = this._helper.GetService>();
+
+ var newGuid = Guid.NewGuid();
+ var contractorUser = EntityHelper.CreateUser(1, newGuid, username: "Test", isContractor: true);
+ contractorUser.PersonId = 1;
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(contractorUser);
+
+ var repository = this._helper.GetService>();
+ repository.Setup(x => x.Add(It.IsAny())).Returns(acqFile);
+ var lookupRepository = this._helper.GetService>();
+ lookupRepository.Setup(x => x.GetAllRegions()).Returns(new List() { new PimsRegion() { Code = 4, RegionName = "Cannot determine" } });
+ var projectRepository = this._helper.GetService>();
+ projectRepository.Setup(x => x.TryGet(It.IsAny())).Returns(acqFile.Project);
+
+ // Act
+ var result = service.Add(acqFile, new List());
+
+ // Assert
+ repository.Verify(x => x.Add(It.IsAny()), Times.Once);
+ }
+
[Fact]
public void Add_ThrowIfNull()
{
@@ -946,6 +978,48 @@ public void Update_Contractor_Removed()
act.Should().Throw();
}
+ [Fact]
+ public void Update_ProjectContractor_Removed()
+ {
+ var service = this.CreateAcquisitionServiceWithPermissions(Permissions.AcquisitionFileEdit);
+
+ var project = new PimsProject() { Id = 1, PimsProjectPeople = new List() { new PimsProjectPerson() { PersonId = 1, ProjectId = 1 } } };
+ var acqFile = EntityHelper.CreateAcquisitionFile();
+ acqFile.ProjectId = 1;
+ acqFile.Project = project;
+
+ var repository = this._helper.GetService>();
+ var userRepository = this._helper.GetService>();
+
+ var contractorUser = EntityHelper.CreateUser(1, Guid.NewGuid(), username: "Test", isContractor: true);
+ contractorUser.PersonId = 1;
+ userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(contractorUser);
+ repository.Setup(x => x.GetById(It.IsAny())).Returns(acqFile);
+ repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1);
+ repository.Setup(x => x.GetRegion(It.IsAny())).Returns(acqFile.RegionCode);
+
+ var projectRepository = this._helper.GetService>();
+ projectRepository.Setup(x => x.TryGet(It.IsAny