diff --git a/source/backend/api/Services/AcquisitionFileService.cs b/source/backend/api/Services/AcquisitionFileService.cs index addc6e49fb..1de3f840bd 100644 --- a/source/backend/api/Services/AcquisitionFileService.cs +++ b/source/backend/api/Services/AcquisitionFileService.cs @@ -223,7 +223,7 @@ public PimsAcquisitionFile Add(PimsAcquisitionFile acquisitionFile, IEnumerable< PopulateAcquisitionChecklist(acquisitionFile); - // Update file specific marker locations + // Update marker locations in the context of this file foreach (var incomingAcquisitionProperty in acquisitionFile.PimsPropertyAcquisitionFiles) { _propertyService.PopulateNewFileProperty(incomingAcquisitionProperty); @@ -304,7 +304,7 @@ public PimsAcquisitionFile UpdateProperties(PimsAcquisitionFile acquisitionFile, throw new BusinessRuleViolationException("The file you are editing is not active or hold, so you cannot save changes. Refresh your browser to see file state."); } - // Get the current properties in the research file + // Get the current properties in the acquisition file var currentFileProperties = _acquisitionFilePropertyRepository.GetPropertiesByAcquisitionFileId(acquisitionFile.Internal_Id); // Check if the property is new or if it is being updated diff --git a/source/backend/api/Services/DispositionFileService.cs b/source/backend/api/Services/DispositionFileService.cs index bdf0b650fa..ef78871ef0 100644 --- a/source/backend/api/Services/DispositionFileService.cs +++ b/source/backend/api/Services/DispositionFileService.cs @@ -75,6 +75,12 @@ public PimsDispositionFile Add(PimsDispositionFile dispositionFile, IEnumerable< MatchProperties(dispositionFile, userOverrides); ValidatePropertyRegions(dispositionFile); + // Update marker locations in the context of this file + foreach (var incomingDispositionProperty in dispositionFile.PimsDispositionFileProperties) + { + _propertyService.PopulateNewFileProperty(incomingDispositionProperty); + } + var newDispositionFile = _dispositionFileRepository.Add(dispositionFile); _dispositionFileRepository.CommitTransaction(); @@ -479,7 +485,7 @@ public List GetDispositionFileExport(DispositionFilt public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile, IEnumerable userOverrides) { - _logger.LogInformation("Updating disposition file properties..."); + _logger.LogInformation("Updating disposition file properties with DispositionFile id: {id}", dispositionFile.Internal_Id); _user.ThrowIfNotAllAuthorized(Permissions.DispositionEdit, Permissions.PropertyView, Permissions.PropertyAdd); _user.ThrowInvalidAccessToDispositionFile(_userRepository, _dispositionFileRepository, dispositionFile.Internal_Id); @@ -489,8 +495,8 @@ public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile, ValidatePropertyRegions(dispositionFile); - // Get the current properties in the research file - var currentProperties = _dispositionFilePropertyRepository.GetPropertiesByDispositionFileId(dispositionFile.Internal_Id); + // Get the current properties in the disposition file + var currentFileProperties = _dispositionFilePropertyRepository.GetPropertiesByDispositionFileId(dispositionFile.Internal_Id); // Check if the property is new or if it is being updated foreach (var incomingDispositionProperty in dispositionFile.PimsDispositionFileProperties) @@ -498,22 +504,37 @@ public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile, // If the property is not new, check if the name has been updated. if (incomingDispositionProperty.Internal_Id != 0) { - PimsDispositionFileProperty existingProperty = currentProperties.FirstOrDefault(x => x.Internal_Id == incomingDispositionProperty.Internal_Id); + var needsUpdate = false; + PimsDispositionFileProperty existingProperty = currentFileProperties.FirstOrDefault(x => x.Internal_Id == incomingDispositionProperty.Internal_Id); if (existingProperty.PropertyName != incomingDispositionProperty.PropertyName) { existingProperty.PropertyName = incomingDispositionProperty.PropertyName; + needsUpdate = true; + } + + var incomingGeom = incomingDispositionProperty.Location; + var existingGeom = existingProperty.Location; + if (existingGeom is null || (incomingGeom is not null && !existingGeom.EqualsExact(incomingGeom))) + { + _propertyService.UpdateFilePropertyLocation(incomingDispositionProperty, existingProperty); + needsUpdate = true; + } + + if (needsUpdate) + { _dispositionFilePropertyRepository.Update(existingProperty); } } else { // New property needs to be added - _dispositionFilePropertyRepository.Add(incomingDispositionProperty); + var newFileProperty = _propertyService.PopulateNewFileProperty(incomingDispositionProperty); + _dispositionFilePropertyRepository.Add(newFileProperty); } } // The ones not on the new set should be deleted - List differenceSet = currentProperties.Where(x => !dispositionFile.PimsDispositionFileProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); + List differenceSet = currentFileProperties.Where(x => !dispositionFile.PimsDispositionFileProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); foreach (var deletedProperty in differenceSet) { _dispositionFilePropertyRepository.Delete(deletedProperty); diff --git a/source/backend/api/Services/IPropertyService.cs b/source/backend/api/Services/IPropertyService.cs index b2b40d39fb..18a52c564b 100644 --- a/source/backend/api/Services/IPropertyService.cs +++ b/source/backend/api/Services/IPropertyService.cs @@ -47,10 +47,11 @@ public interface IPropertyService void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty propertyToUpdate, IEnumerable overrideCodes); - T PopulateNewFileProperty(T fileProperty); + T PopulateNewFileProperty(T fileProperty) + where T : IFilePropertyEntity; void UpdateFilePropertyLocation(T incomingFileProperty, T filePropertyToUpdate) - where T : IWithPropertyEntity; + where T : IFilePropertyEntity; IList GetHistoricalNumbersForPropertyId(long propertyId); @@ -71,6 +72,6 @@ void UpdateFilePropertyLocation(T incomingFileProperty, T filePropertyToUpdat /// The file properties to re-project. /// The file properties with transformed spatial locations. List TransformAllPropertiesToLatLong(List fileProperties) - where T : IWithPropertyEntity; + where T : IFilePropertyEntity; } } diff --git a/source/backend/api/Services/LeaseService.cs b/source/backend/api/Services/LeaseService.cs index 9f85449c4b..3f9022177a 100644 --- a/source/backend/api/Services/LeaseService.cs +++ b/source/backend/api/Services/LeaseService.cs @@ -180,11 +180,17 @@ public PimsLease Add(PimsLease lease, IEnumerable userOverride var pimsUser = _userRepository.GetByKeycloakUserId(_user.GetUserKey()); pimsUser.ThrowInvalidAccessToLeaseFile(lease.RegionCode); - var leasesWithProperties = AssociatePropertyLeases(lease, userOverrides); + var leaseWithProperties = AssociatePropertyLeases(lease, userOverrides); lease.PimsLeaseChecklistItems = GetActiveChecklistItemsForLease(); - return _leaseRepository.Add(leasesWithProperties); + // Update marker locations in the context of this file + foreach (var incomingLeaseProperty in leaseWithProperties.PimsPropertyLeases) + { + _propertyService.PopulateNewFileProperty(incomingLeaseProperty); + } + + return _leaseRepository.Add(leaseWithProperties); } public IEnumerable GetPropertiesByLeaseId(long leaseId) @@ -209,8 +215,8 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr pimsUser.ThrowInvalidAccessToLeaseFile(currentLease.RegionCode); // need to check that the user is able to access the current lease as well as has the region for the updated lease. pimsUser.ThrowInvalidAccessToLeaseFile(lease.RegionCode); - var currentProperties = _propertyLeaseRepository.GetAllByLeaseId(lease.LeaseId); - var newPropertiesAdded = lease.PimsPropertyLeases.Where(x => !currentProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); + var currentFileProperties = _propertyLeaseRepository.GetAllByLeaseId(lease.LeaseId); + var newPropertiesAdded = lease.PimsPropertyLeases.Where(x => !currentFileProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); if (newPropertiesAdded.Any(x => x.Property.IsRetired.HasValue && x.Property.IsRetired.Value)) { @@ -228,13 +234,37 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr _leaseRepository.Update(lease, false); var leaseWithProperties = AssociatePropertyLeases(lease, userOverrides); + + // Update marker locations in the context of this file + foreach (var incomingLeaseProperty in leaseWithProperties.PimsPropertyLeases) + { + // If the property is not new, check if the marker location has been updated. + if (incomingLeaseProperty.Internal_Id != 0) + { + var existingFileProperty = currentFileProperties.FirstOrDefault(x => x.Internal_Id == incomingLeaseProperty.Internal_Id); + + var incomingGeom = incomingLeaseProperty?.Location; + var existingGeom = existingFileProperty?.Location; + if (existingGeom is null || (incomingGeom is not null && !existingGeom.EqualsExact(incomingGeom))) + { + _propertyService.UpdateFilePropertyLocation(incomingLeaseProperty, existingFileProperty); + incomingLeaseProperty.Location = existingFileProperty?.Location; + } + } + else + { + // New property needs to be added + _propertyService.PopulateNewFileProperty(incomingLeaseProperty); + } + } + _propertyLeaseRepository.UpdatePropertyLeases(lease.Internal_Id, leaseWithProperties.PimsPropertyLeases); _leaseRepository.UpdateLeaseConsultations(lease.Internal_Id, lease.ConcurrencyControlNumber, lease.PimsLeaseConsultations); _leaseRepository.UpdateLeaseRenewals(lease.Internal_Id, lease.ConcurrencyControlNumber, lease.PimsLeaseRenewals); - List differenceSet = currentProperties.Where(x => !lease.PimsPropertyLeases.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); + List differenceSet = currentFileProperties.Where(x => !lease.PimsPropertyLeases.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); foreach (var deletedProperty in differenceSet) { var totalAssociationCount = _propertyRepository.GetAllAssociationsCountById(deletedProperty.PropertyId); diff --git a/source/backend/api/Services/PropertyService.cs b/source/backend/api/Services/PropertyService.cs index 2b16a263d1..3b010f3d50 100644 --- a/source/backend/api/Services/PropertyService.cs +++ b/source/backend/api/Services/PropertyService.cs @@ -426,37 +426,31 @@ public void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty prope } } + /// public T PopulateNewFileProperty(T fileProperty) + where T : IFilePropertyEntity { - // TODO: Remove this casting when LOCATION gets added to all remaining file-property types (research, disposition, lease) - if (fileProperty is PimsPropertyAcquisitionFile acquisitionFileProperty) + // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage + var geom = fileProperty.Location; + if (geom is not null && geom.SRID != SpatialReference.BCALBERS) { - // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage - var geom = acquisitionFileProperty.Location; - if (geom is not null && geom.SRID != SpatialReference.BCALBERS) - { - var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); - acquisitionFileProperty.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); - } + var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); + fileProperty.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); } return fileProperty; } + /// public void UpdateFilePropertyLocation(T incomingFileProperty, T filePropertyToUpdate) - where T : IWithPropertyEntity + where T : IFilePropertyEntity { - // TODO: Remove this casting when LOCATION gets added to all remaining file-property types (research, disposition, lease) - if (incomingFileProperty is PimsPropertyAcquisitionFile incomingAcquisitionProperty - && filePropertyToUpdate is PimsPropertyAcquisitionFile acquisitionPropertyToUpdate) + // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage + var geom = incomingFileProperty.Location; + if (geom is not null && geom.SRID != SpatialReference.BCALBERS) { - // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage - var geom = incomingAcquisitionProperty.Location; - if (geom is not null && geom.SRID != SpatialReference.BCALBERS) - { - var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); - acquisitionPropertyToUpdate.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); - } + var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); + filePropertyToUpdate.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); } } @@ -494,14 +488,13 @@ public IList UpdateHistoricalFileNumbers(long property /// public List TransformAllPropertiesToLatLong(List fileProperties) - where T : IWithPropertyEntity + where T : IFilePropertyEntity { foreach (var fileProperty in fileProperties) { - // TODO: Remove this casting when LOCATION gets added to all remaining file-property types (research, disposition, lease) - if (fileProperty is PimsPropertyAcquisitionFile acquisitionFileProperty && acquisitionFileProperty.Location is not null) + if (fileProperty.Location is not null) { - acquisitionFileProperty.Location = TransformCoordinates(acquisitionFileProperty.Location); + fileProperty.Location = TransformCoordinates(fileProperty.Location); } TransformPropertyToLatLong(fileProperty.Property); diff --git a/source/backend/api/Services/ResearchFileService.cs b/source/backend/api/Services/ResearchFileService.cs index 80d9977ef1..2df7c1311d 100644 --- a/source/backend/api/Services/ResearchFileService.cs +++ b/source/backend/api/Services/ResearchFileService.cs @@ -74,6 +74,12 @@ public PimsResearchFile Add(PimsResearchFile researchFile, IEnumerable userOverrideCodes) { - _logger.LogInformation("Updating research file properties..."); - _user.ThrowIfNotAuthorized(Permissions.ResearchFileEdit); + _logger.LogInformation("Updating research file properties with ResearchFile id: {id}", researchFile.Internal_Id); + _user.ThrowIfNotAllAuthorized(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); + ValidateVersion(researchFile.Internal_Id, researchFile.ConcurrencyControlNumber); MatchProperties(researchFile, userOverrideCodes); // Get the current properties in the research file - var currentProperties = _researchFilePropertyRepository.GetAllByResearchFileId(researchFile.Internal_Id); + var currentFileProperties = _researchFilePropertyRepository.GetAllByResearchFileId(researchFile.Internal_Id); // Check if the property is new or if it is being updated foreach (var incomingResearchProperty in researchFile.PimsPropertyResearchFiles) @@ -111,22 +118,37 @@ public PimsResearchFile UpdateProperties(PimsResearchFile researchFile, IEnumera // If the property is not new, check if the name has been updated. if (incomingResearchProperty.Internal_Id != 0) { - PimsPropertyResearchFile existingProperty = currentProperties.FirstOrDefault(x => x.Internal_Id == incomingResearchProperty.Internal_Id); + var needsUpdate = false; + PimsPropertyResearchFile existingProperty = currentFileProperties.FirstOrDefault(x => x.Internal_Id == incomingResearchProperty.Internal_Id); if (existingProperty.PropertyName != incomingResearchProperty.PropertyName) { existingProperty.PropertyName = incomingResearchProperty.PropertyName; + needsUpdate = true; + } + + var incomingGeom = incomingResearchProperty.Location; + var existingGeom = existingProperty.Location; + if (existingGeom is null || (incomingGeom is not null && !existingGeom.EqualsExact(incomingGeom))) + { + _propertyService.UpdateFilePropertyLocation(incomingResearchProperty, existingProperty); + needsUpdate = true; + } + + if (needsUpdate) + { _researchFilePropertyRepository.Update(existingProperty); } } else { // New property needs to be added - _researchFilePropertyRepository.Add(incomingResearchProperty); + var newFileProperty = _propertyService.PopulateNewFileProperty(incomingResearchProperty); + _researchFilePropertyRepository.Add(newFileProperty); } } // The ones not on the new set should be deleted - List differenceSet = currentProperties.Where(x => !researchFile.PimsPropertyResearchFiles.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); + List differenceSet = currentFileProperties.Where(x => !researchFile.PimsPropertyResearchFiles.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); foreach (var deletedProperty in differenceSet) { _researchFilePropertyRepository.Delete(deletedProperty); diff --git a/source/backend/apimodels/Models/Concepts/DispositionFile/DispositionFilePropertyMap.cs b/source/backend/apimodels/Models/Concepts/DispositionFile/DispositionFilePropertyMap.cs index 10cc772add..bbe85d8e05 100644 --- a/source/backend/apimodels/Models/Concepts/DispositionFile/DispositionFilePropertyMap.cs +++ b/source/backend/apimodels/Models/Concepts/DispositionFile/DispositionFilePropertyMap.cs @@ -10,6 +10,7 @@ public void Register(TypeAdapterConfig config) { config.NewConfig() .Map(dest => dest.Id, src => src.DispositionFilePropertyId) + .Map(dest => dest.Location, src => src.Location) .Map(dest => dest.PropertyName, src => src.PropertyName) .Map(dest => dest.Property, src => src.Property) .Map(dest => dest.PropertyId, src => src.PropertyId) @@ -19,6 +20,7 @@ public void Register(TypeAdapterConfig config) config.NewConfig() .Map(dest => dest.DispositionFilePropertyId, src => src.Id) + .Map(dest => dest.Location, src => src.Location) .Map(dest => dest.PropertyName, src => src.PropertyName) .Map(dest => dest.Property, src => src.Property) .Map(dest => dest.PropertyId, src => src.Property.Id) diff --git a/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseMap.cs b/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseMap.cs index 1559a9b1f2..0232f2acac 100644 --- a/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseMap.cs +++ b/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseMap.cs @@ -1,4 +1,5 @@ using Mapster; +using Pims.Api.Models.Base; using Pims.Dal.Entities; namespace Pims.Api.Models.Concepts.Lease @@ -16,8 +17,9 @@ public void Register(TypeAdapterConfig config) .Map(dest => dest.AreaUnitType, src => src.AreaUnitTypeCodeNavigation) .Map(dest => dest.LeaseArea, src => src.LeaseArea) .Map(dest => dest.PropertyName, src => src.Name) - .Map(dest => dest.RowVersion, src => src.ConcurrencyControlNumber) - .Map(dest => dest.Id, src => src.Internal_Id); + .Map(dest => dest.Location, src => src.Location) + .Map(dest => dest.Id, src => src.Internal_Id) + .Inherits(); config.NewConfig() .PreserveReference(true) @@ -26,8 +28,9 @@ public void Register(TypeAdapterConfig config) .Map(dest => dest.AreaUnitTypeCode, src => src.AreaUnitType.Id) .Map(dest => dest.LeaseArea, src => src.LeaseArea) .Map(dest => dest.Name, src => src.PropertyName) - .Map(dest => dest.ConcurrencyControlNumber, src => src.RowVersion) - .Map(dest => dest.Internal_Id, src => src.Id); + .Map(dest => dest.Location, src => src.Location) + .Map(dest => dest.Internal_Id, src => src.Id) + .Inherits(); } } } diff --git a/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseModel.cs b/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseModel.cs index 8275c4c3ad..5b5bc06043 100644 --- a/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseModel.cs +++ b/source/backend/apimodels/Models/Concepts/Lease/PropertyLeaseModel.cs @@ -5,7 +5,7 @@ namespace Pims.Api.Models.Concepts.Lease { public class PropertyLeaseModel : FilePropertyModel { - #region Propertie + #region Properties public new LeaseModel File { get; set; } diff --git a/source/backend/apimodels/Models/Concepts/ResearchFile/ResearchFilePropertyMap.cs b/source/backend/apimodels/Models/Concepts/ResearchFile/ResearchFilePropertyMap.cs index 9138ee8949..29c22c86ac 100644 --- a/source/backend/apimodels/Models/Concepts/ResearchFile/ResearchFilePropertyMap.cs +++ b/source/backend/apimodels/Models/Concepts/ResearchFile/ResearchFilePropertyMap.cs @@ -12,6 +12,7 @@ public void Register(TypeAdapterConfig config) .Map(dest => dest.Id, src => src.PropertyResearchFileId) .Map(dest => dest.PropertyName, src => src.PropertyName) .Map(dest => dest.DisplayOrder, src => src.DisplayOrder) + .Map(dest => dest.Location, src => src.Location) .Map(dest => dest.IsLegalOpinionRequired, src => src.IsLegalOpinionRequired) .Map(dest => dest.IsLegalOpinionObtained, src => src.IsLegalOpinionObtained) .Map(dest => dest.DocumentReference, src => src.DocumentReference) @@ -30,6 +31,7 @@ public void Register(TypeAdapterConfig config) .Map(dest => dest.ResearchFileId, src => src.FileId) .Map(dest => dest.PropertyName, src => src.PropertyName) .Map(dest => dest.DisplayOrder, src => src.DisplayOrder) + .Map(dest => dest.Location, src => src.Location) .Map(dest => dest.IsLegalOpinionRequired, src => src.IsLegalOpinionRequired) .Map(dest => dest.IsLegalOpinionObtained, src => src.IsLegalOpinionObtained) .Map(dest => dest.DocumentReference, src => src.DocumentReference) diff --git a/source/backend/dal/Repositories/PropertyLeaseRepository.cs b/source/backend/dal/Repositories/PropertyLeaseRepository.cs index 3b939adb05..9b565c5143 100644 --- a/source/backend/dal/Repositories/PropertyLeaseRepository.cs +++ b/source/backend/dal/Repositories/PropertyLeaseRepository.cs @@ -80,9 +80,8 @@ public IEnumerable GetAllByLeaseId(long leaseId) /// public IEnumerable UpdatePropertyLeases(long leaseId, ICollection pimsPropertyLeases) { - this.User.ThrowIfNotAuthorized(Permissions.LeaseEdit); - - this.Context.UpdateChild(l => l.PimsPropertyLeases, leaseId, pimsPropertyLeases.ToArray()); + User.ThrowIfNotAuthorized(Permissions.LeaseEdit); + Context.UpdateChild(l => l.PimsPropertyLeases, leaseId, pimsPropertyLeases.ToArray()); return GetAllByLeaseId(leaseId); } diff --git a/source/backend/entities/Partials/DispositionFileProperty.cs b/source/backend/entities/Partials/DispositionFileProperty.cs index 30f6930cfd..38976f8629 100644 --- a/source/backend/entities/Partials/DispositionFileProperty.cs +++ b/source/backend/entities/Partials/DispositionFileProperty.cs @@ -5,7 +5,7 @@ namespace Pims.Dal.Entities /// /// PimsDispositionFileProperty class, provides an entity for the datamodel to manage the relationship between the Disposition Files Properties. /// - public partial class PimsDispositionFileProperty : StandardIdentityBaseAppEntity, IBaseAppEntity, IWithPropertyEntity + public partial class PimsDispositionFileProperty : StandardIdentityBaseAppEntity, IBaseAppEntity, IFilePropertyEntity { #region Properties [NotMapped] diff --git a/source/backend/entities/Partials/PropertyAcquisitionFile.cs b/source/backend/entities/Partials/PropertyAcquisitionFile.cs index 6151b4e366..2c4bc53e08 100644 --- a/source/backend/entities/Partials/PropertyAcquisitionFile.cs +++ b/source/backend/entities/Partials/PropertyAcquisitionFile.cs @@ -5,7 +5,7 @@ namespace Pims.Dal.Entities /// /// PimsPropertyAcquisitionFile class, provides an entity for the datamodel to manage the relationship between Properties and Acquisition Files. /// - public partial class PimsPropertyAcquisitionFile : StandardIdentityBaseAppEntity, IBaseAppEntity, IWithPropertyEntity + public partial class PimsPropertyAcquisitionFile : StandardIdentityBaseAppEntity, IBaseAppEntity, IFilePropertyEntity { #region Properties [NotMapped] diff --git a/source/backend/entities/Partials/PropertyLease.cs b/source/backend/entities/Partials/PropertyLease.cs index 6416c88449..d6b3c643ef 100644 --- a/source/backend/entities/Partials/PropertyLease.cs +++ b/source/backend/entities/Partials/PropertyLease.cs @@ -6,7 +6,7 @@ namespace Pims.Dal.Entities /// /// PimsPropertyLease class, provides the many-to-many relationship between leases and properties. /// - public partial class PimsPropertyLease : StandardIdentityBaseAppEntity, IBaseAppEntity, IWithPropertyEntity + public partial class PimsPropertyLease : StandardIdentityBaseAppEntity, IBaseAppEntity, IFilePropertyEntity { #region Properties [NotMapped] diff --git a/source/backend/entities/Partials/PropertyResearchFile.cs b/source/backend/entities/Partials/PropertyResearchFile.cs index bf3df14cf4..6a31aa66c4 100644 --- a/source/backend/entities/Partials/PropertyResearchFile.cs +++ b/source/backend/entities/Partials/PropertyResearchFile.cs @@ -5,7 +5,7 @@ namespace Pims.Dal.Entities /// /// PimsPropertyResearchFile class, provides an entity for the datamodel to manage the relationship between Properties and Research Files. /// - public partial class PimsPropertyResearchFile : StandardIdentityBaseAppEntity, IBaseAppEntity, IWithPropertyEntity + public partial class PimsPropertyResearchFile : StandardIdentityBaseAppEntity, IBaseAppEntity, IFilePropertyEntity { #region Properties [NotMapped] diff --git a/source/backend/entities/Partials/base/IWithPropertyEntity.cs b/source/backend/entities/Partials/base/IFilePropertyEntity.cs similarity index 69% rename from source/backend/entities/Partials/base/IWithPropertyEntity.cs rename to source/backend/entities/Partials/base/IFilePropertyEntity.cs index fde3daec10..72d38e1873 100644 --- a/source/backend/entities/Partials/base/IWithPropertyEntity.cs +++ b/source/backend/entities/Partials/base/IFilePropertyEntity.cs @@ -1,9 +1,11 @@ +using NetTopologySuite.Geometries; + namespace Pims.Dal.Entities { /// /// An interface for entities that have an associated PimsProperty (ie - LeaseProperty, ResearchProperty, AcquisitionProperty, DispositionProperty, etc). /// - public interface IWithPropertyEntity + public interface IFilePropertyEntity { /// /// get/set - The property entity for this relationship. @@ -14,5 +16,10 @@ public interface IWithPropertyEntity /// get/set - The property id. /// long PropertyId { get; set; } + + /// + /// Geo-spatial location (pin) of property. + /// + public Geometry Location { get; set; } } } diff --git a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs index 53a5b954b3..223a25bc6a 100644 --- a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs @@ -1276,7 +1276,7 @@ public void UpdateProperties_MatchProperties_Success() acqFile.ConcurrencyControlNumber = 1; var property = EntityHelper.CreateProperty(12345, regionCode: 1); - acqFile.PimsPropertyAcquisitionFiles = new List() { new PimsPropertyAcquisitionFile() { Property = property } }; + acqFile.PimsPropertyAcquisitionFiles = new List() { new PimsPropertyAcquisitionFile() { Internal_Id = 1, Property = property } }; var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); @@ -1303,7 +1303,10 @@ public void UpdateProperties_MatchProperties_Success() // Assert filePropertyRepository.Verify(x => x.GetPropertiesByAcquisitionFileId(It.IsAny()), Times.Once); + filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); + } [Fact] @@ -1368,6 +1371,7 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() filePropertyRepository.Verify(x => x.GetPropertiesByAcquisitionFileId(It.IsAny()), Times.Once); propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } [Fact] diff --git a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs index 8481fb11a2..a84bd3c8f1 100644 --- a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs @@ -245,6 +245,7 @@ public void Add_ThrowIfNull() var dspFile = EntityHelper.CreateDispositionFile(); var repository = this._helper.GetService>(); + var propertyService = this._helper.GetService>(); // Act Action act = () => service.Add(null, new List()); @@ -252,6 +253,7 @@ public void Add_ThrowIfNull() // Assert act.Should().Throw(); repository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } [Fact] @@ -263,11 +265,16 @@ public void Add_Fails_Duplicate_Team() dispFile.PimsDispositionFileTeams.Add(new PimsDispositionFileTeam() { PersonId = 1, DspFlTeamProfileTypeCode = "LISTAGENT" }); dispFile.PimsDispositionFileTeams.Add(new PimsDispositionFileTeam() { PersonId = 2, DspFlTeamProfileTypeCode = "LISTAGENT" }); + var repository = this._helper.GetService>(); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(dispFile, new List() { UserOverrideCode.AddPropertyToInventory }); // Assert act.Should().Throw(); + repository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } [Fact] @@ -282,11 +289,16 @@ public void Add_ContractorNotInTeamException_Fail_IsContractor() var contractorUser = EntityHelper.CreateUser(1, Guid.NewGuid(), username: "Test", isContractor: true); userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(contractorUser); + var repository = this._helper.GetService>(); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(dispositionFile, new List() { UserOverrideCode.UpdateRegion }); // Assert var ex = act.Should().Throw(); + repository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } [Fact] @@ -318,6 +330,39 @@ public void Add_Success_IsContractor_AssignedToTeam() repository.Verify(x => x.Add(It.IsAny()), Times.Once); } + [Fact] + public void Add_Success_FileMarkerLocation() + { + // Arrange + var service = this.CreateDispositionServiceWithPermissions(Permissions.DispositionAdd); + + var dispositionFile = EntityHelper.CreateDispositionFile(); + var property = EntityHelper.CreateProperty(1000, regionCode: 1); + + dispositionFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Property = property } }; + + var repository = this._helper.GetService>(); + repository.Setup(x => x.Add(It.IsAny())).Returns(dispositionFile); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(property); + + var userRepository = this._helper.GetService>(); + userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); + + var lookupRepository = this._helper.GetService>(); + lookupRepository.Setup(x => x.GetAllRegions()).Returns(new List() { new PimsRegion() { Code = 4, RegionName = "Cannot determine" } }); + + var propertyService = this._helper.GetService>(); + + // Act + var result = service.Add(dispositionFile, new List()); + + // Assert + repository.Verify(x => x.Add(It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); + } + [Fact] public void Add_WithRetiredProperty_Should_Fail() { @@ -345,12 +390,16 @@ public void Add_WithRetiredProperty_Should_Fail() var propertyRepository = this._helper.GetService>(); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(pimsProperty); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(dispositionFile, new List()); // Assert var ex = act.Should().Throw(); ex.WithMessage("Retired property can not be selected."); + repository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } #endregion @@ -929,7 +978,7 @@ public void UpdateProperties_MatchProperties_Success() dspFile.ConcurrencyControlNumber = 1; var property = EntityHelper.CreateProperty(12345, regionCode: 1); - dspFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Property = property } }; + dspFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Internal_Id = 1, Property = property } }; var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); @@ -947,13 +996,16 @@ public void UpdateProperties_MatchProperties_Success() var propertyService = this._helper.GetService>(); propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act service.UpdateProperties(dspFile, new List() { UserOverrideCode.AddLocationToProperty }); // Assert filePropertyRepository.Verify(x => x.GetPropertiesByDispositionFileId(It.IsAny()), Times.Once); + filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -968,8 +1020,9 @@ public void UpdateProperties_MatchProperties_NewProperty_UserOverride() var property = EntityHelper.CreateProperty(12345, regionCode: 1); dspFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Property = property } }; - var repository = this._helper.GetService>(); PimsDispositionFileProperty updatedDispositionFileProperty = null; + + var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); @@ -980,9 +1033,6 @@ public void UpdateProperties_MatchProperties_NewProperty_UserOverride() var propertyRepository = this._helper.GetService>(); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Throws(); - var coordinateService = this._helper.GetService>(); - coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new Coordinate(924046.3314288399, 1088892.9140135897)); - var propertyService = this._helper.GetService>(); propertyService.Setup(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new PimsProperty() { @@ -994,6 +1044,7 @@ public void UpdateProperties_MatchProperties_NewProperty_UserOverride() SurplusDeclarationTypeCode = "UNKNOWN", RegionCode = 1 }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); var userRepository = this._helper.GetService>(); userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); @@ -1018,8 +1069,9 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() var property = EntityHelper.CreateProperty(12345, regionCode: 1); dspFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Property = property } }; - var repository = this._helper.GetService>(); PimsDispositionFileProperty updatedDispositionFileProperty = null; + + var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); @@ -1044,6 +1096,7 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() SurplusDeclarationTypeCode = "UNKNOWN", RegionCode = 1 }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); var userRepository = this._helper.GetService>(); userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); @@ -1063,7 +1116,9 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() updatedProperty.IsOwned.Should().Be(false); filePropertyRepository.Verify(x => x.GetPropertiesByDispositionFileId(It.IsAny()), Times.Once); + filePropertyRepository.Verify(x => x.Add(It.IsAny()), Times.Once); propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } [Fact] @@ -1089,6 +1144,8 @@ public void UpdateProperties_UpdatePropertyFile_Success() propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Throws(); propertyRepository.Setup(x => x.GetPropertyRegion(It.IsAny())).Returns(1); + var propertyService = this._helper.GetService>(); + var userRepository = this._helper.GetService>(); userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); @@ -1097,6 +1154,7 @@ public void UpdateProperties_UpdatePropertyFile_Success() // Assert filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); + propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -1115,7 +1173,7 @@ public void UpdateProperties_RemovePropertyFile_Success() repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); var filePropertyRepository = this._helper.GetService>(); - filePropertyRepository.Setup(x => x.GetPropertiesByDispositionFileId(It.IsAny())).Returns(new List() { new PimsDispositionFileProperty() { Property = property } }); + filePropertyRepository.Setup(x => x.GetPropertiesByDispositionFileId(It.IsAny())).Returns(new List() { new PimsDispositionFileProperty() { Internal_Id = 1, Property = property } }); var propertyRepository = this._helper.GetService>(); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), false)).Throws(); @@ -1177,15 +1235,15 @@ public void UpdateProperties_NoPermission() var dspFile = EntityHelper.CreateDispositionFile(); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(dspFile); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); + repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); // Act Action act = () => service.UpdateProperties(dspFile, new List()); // Assert act.Should().Throw(); - repository.Verify(x => x.Update(It.IsAny(), It.IsAny()), Times.Never); + repository.Verify(x => x.GetById(It.IsAny()), Times.Never); } [Fact] @@ -1197,21 +1255,19 @@ public void UpdateProperties_NotAuthorized_Contractor() var dspFile = EntityHelper.CreateDispositionFile(); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(dspFile); + repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); var userRepository = this._helper.GetService>(); var contractorUser = EntityHelper.CreateUser(1, Guid.NewGuid(), username: "Test", isContractor: true); userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(contractorUser); - var dspFileRepository = this._helper.GetService>(); - dspFileRepository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); - // Act Action act = () => service.UpdateProperties(dspFile, new List()); // Assert act.Should().Throw(); + repository.Verify(x => x.GetById(It.IsAny()), Times.Never); } [Fact] diff --git a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs index 4f7af99823..73aec8832d 100644 --- a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs +++ b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs @@ -76,6 +76,9 @@ public void Add_Success() var propertyRepository = this._helper.GetService>(); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(lease.PimsPropertyLeases.FirstOrDefault().Property); + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); + var userRepository = this._helper.GetService>(); userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(user); @@ -87,7 +90,9 @@ public void Add_Success() result.Should().BeAssignableTo(); result.LeaseId.Should().Be(1); result.PimsLeaseChecklistItems.Should().HaveCount(1); + leaseRepository.Verify(x => x.Add(It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } [Fact] @@ -101,12 +106,15 @@ public void Add_NoPermission() var leaseRepository = this._helper.GetService>(); leaseRepository.Setup(x => x.Add(It.IsAny())).Returns(lease); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(lease, new List()); // Assert act.Should().Throw(); leaseRepository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } [Fact] @@ -124,12 +132,15 @@ public void Add_InvalidAccessToLeaseFile() leaseRepository.Setup(x => x.Add(It.IsAny())).Returns(lease); userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(user); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(lease, new List()); // Assert act.Should().Throw(); leaseRepository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } [Fact] @@ -164,12 +175,17 @@ public void Add_WithRetiredProperty_Should_Fail() var userRepository = this._helper.GetService>(); userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(user); + var propertyService = this._helper.GetService>(); + // Act Action act = () => service.Add(lease, new List()); // Assert var ex = act.Should().Throw(); ex.WithMessage("Retired property can not be selected."); + + leaseRepository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); } #endregion @@ -374,7 +390,7 @@ public void UpdateProperties_Success() // Arrange var lease = EntityHelper.CreateLease(1); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); @@ -402,7 +418,7 @@ public void UpdateProperties_WithRetiredProperty_Should_Fail() // Arrange var lease = EntityHelper.CreateLease(1); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); @@ -434,7 +450,7 @@ public void UpdateProperties_MatchProperties_Success() // Arrange var lease = EntityHelper.CreateLease(1); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); @@ -462,7 +478,7 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() // Arrange var lease = EntityHelper.CreateLease(1); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView, Permissions.PropertyAdd, Permissions.PropertyView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); @@ -491,6 +507,7 @@ public void UpdateProperties_MatchProperties_NewProperty_Success() newProperty.RegionCode = 1; }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); // Act var updatedLease = service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); @@ -518,7 +535,7 @@ public void UpdateProperties_RemovePropertyFile_Success() var lease = EntityHelper.CreateLease(1); var updatedLease = EntityHelper.CreateLease(2, addProperty: false); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); @@ -546,7 +563,7 @@ public void UpdateProperties_RemoveProperty_Success() var deletedProperty = lease.PimsPropertyLeases.FirstOrDefault().Property; var updatedLease = EntityHelper.CreateLease(2, addProperty: false); - var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.LeaseView); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); var leaseRepository = this._helper.GetService>(); var propertyLeaseRepository = this._helper.GetService>(); var propertyRepository = this._helper.GetService>(); diff --git a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs index b44476527a..4942475634 100644 --- a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs @@ -153,7 +153,7 @@ public void UpdateProperties_Delete() pimsPropertyResearchFile.PimsPrfPropResearchPurposeTypes = new List() { new PimsPrfPropResearchPurposeType() { } }; researchFile.PimsPropertyResearchFiles.Add(pimsPropertyResearchFile); - var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit); + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); var researchRepository = this._helper.GetService>(); researchRepository.Setup(x => x.GetPage(It.IsAny())); researchRepository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(2); @@ -175,13 +175,13 @@ public void UpdateProperties_Delete() public void UpdateProperties_MatchProperties_PID_Success() { // Arrange - var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit); + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); var researchFile = EntityHelper.CreateResearchFile(); researchFile.ConcurrencyControlNumber = 1; var property = EntityHelper.CreateProperty(12345); - researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Property = property } }; + researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Internal_Id = 1, Property = property } }; var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); @@ -195,27 +195,29 @@ public void UpdateProperties_MatchProperties_PID_Success() var propertyService = this._helper.GetService>(); propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act service.UpdateProperties(researchFile, new List() { UserOverrideCode.AddLocationToProperty }); // Assert - filePropertyRepository.Verify(x => x.Add(It.IsAny()), Times.Once); + filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } [Fact] public void UpdateProperties_MatchProperties_PIN_Success() { // Arrange - var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit); + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); var researchFile = EntityHelper.CreateResearchFile(); researchFile.ConcurrencyControlNumber = 1; var property = EntityHelper.CreateProperty(12345, 54321); property.Pid = null; - researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Property = property } }; + researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Internal_Id = 1, Property = property } }; var repository = this._helper.GetService>(); repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); @@ -229,20 +231,22 @@ public void UpdateProperties_MatchProperties_PIN_Success() var propertyService = this._helper.GetService>(); propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act service.UpdateProperties(researchFile, new List() { UserOverrideCode.AddLocationToProperty }); // Assert - filePropertyRepository.Verify(x => x.Add(It.IsAny()), Times.Once); + filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } [Fact] public void UpdateProperties_MatchProperties_PID_NewProperty_Success() { // Arrange - var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit); + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); var researchFile = EntityHelper.CreateResearchFile(); researchFile.ConcurrencyControlNumber = 1; @@ -272,13 +276,11 @@ public void UpdateProperties_MatchProperties_PID_NewProperty_Success() SurplusDeclarationTypeCode = "UNKNOWN", RegionCode = 1 }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); var propertyRepository = this._helper.GetService>(); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Throws(); - var coordinateService = this._helper.GetService>(); - coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new Coordinate(924046.3314288399, 1088892.9140135897)); - // Act service.UpdateProperties(researchFile, new List()); @@ -293,14 +295,15 @@ public void UpdateProperties_MatchProperties_PID_NewProperty_Success() updatedProperty.PropertyDataSourceTypeCode.Should().Be("PMBC"); updatedProperty.IsOwned.Should().Be(false); - propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny())); + propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } [Fact] public void UpdateProperties_MatchProperties_PIN_NewProperty_Success() { // Arrange - var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit); + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); var researchFile = EntityHelper.CreateResearchFile(); researchFile.ConcurrencyControlNumber = 1; @@ -333,9 +336,7 @@ public void UpdateProperties_MatchProperties_PIN_NewProperty_Success() SurplusDeclarationTypeCode = "UNKNOWN", RegionCode = 1 }); - - var coordinateService = this._helper.GetService>(); - coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new Coordinate(924046.3314288399, 1088892.9140135897)); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); // Act service.UpdateProperties(researchFile, new List()); @@ -351,7 +352,8 @@ public void UpdateProperties_MatchProperties_PIN_NewProperty_Success() updatedProperty.PropertyDataSourceTypeCode.Should().Be("PMBC"); updatedProperty.IsOwned.Should().Be(false); - propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny())); + propertyService.Verify(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } #endregion diff --git a/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx index a1ba4f94dc..c2f9d84251 100644 --- a/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx +++ b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx @@ -90,7 +90,9 @@ describe('LeaseFeeDeterminationSubForm component', () => { await fillInput(container, 'isFinancialGain', 'true', 'select'); }); - expect(suggestedFeeField).toHaveTextContent('Fair Market Value (FMV) - (Licence Administration Fee Minimum)'); + expect(suggestedFeeField).toHaveTextContent( + 'Fair Market Value (FMV) - (Licence Administration Fee Minimum)', + ); }); it('displays expected LAF fee', async () => { diff --git a/source/frontend/src/features/leases/detail/LeasePages/details/DetailFeeDetermination.test.tsx b/source/frontend/src/features/leases/detail/LeasePages/details/DetailFeeDetermination.test.tsx index ac624262eb..de819610c2 100644 --- a/source/frontend/src/features/leases/detail/LeasePages/details/DetailFeeDetermination.test.tsx +++ b/source/frontend/src/features/leases/detail/LeasePages/details/DetailFeeDetermination.test.tsx @@ -96,7 +96,9 @@ describe('DetailFeeDetermination component', () => { isFinancialGain: true, }, }); - expect(getByText('Fair Market Value (FMV) - (Licence Administration Fee Minimum)')).toBeVisible(); + expect( + getByText('Fair Market Value (FMV) - (Licence Administration Fee Minimum)'), + ).toBeVisible(); }); it('renders the suggested Fee field with non-defined calculation', () => { @@ -126,4 +128,4 @@ describe('DetailFeeDetermination component', () => { expect(suggestedFeeField).toHaveTextContent('Unknown'); }); -}); \ No newline at end of file +}); diff --git a/source/frontend/src/features/leases/models.ts b/source/frontend/src/features/leases/models.ts index 99df513515..cbd8b5147f 100644 --- a/source/frontend/src/features/leases/models.ts +++ b/source/frontend/src/features/leases/models.ts @@ -213,7 +213,9 @@ export class LeaseFormModel { motiName: formLease.motiName, hasDigitalLicense: formLease.hasDigitalLicense ?? null, hasPhysicalLicense: formLease.hasPhysicalLicense ?? null, - fileProperties: formLease.properties?.map(p => FormLeaseProperty.toApi(p)), + fileProperties: formLease.properties + ?.map(p => FormLeaseProperty.toApi(p)) + .filter(x => exists(x)), isResidential: formLease.isResidential, isCommercialBuilding: formLease.isCommercialBuilding, isOtherImprovement: formLease.isOtherImprovement, @@ -292,20 +294,24 @@ export class FormLeaseProperty { return model; } - public static toApi(formLeaseProperty: FormLeaseProperty): ApiGen_Concepts_PropertyLease { + public static toApi(formLeaseProperty: FormLeaseProperty): ApiGen_Concepts_PropertyLease | null { + if (!exists(formLeaseProperty?.property)) { + return null; + } + + const apiFileProperty = formLeaseProperty?.property?.toFilePropertyApi( + formLeaseProperty.leaseId, + ); + return { + ...apiFileProperty, id: formLeaseProperty.id ?? 0, - fileId: formLeaseProperty.leaseId ?? 0, file: null, - property: formLeaseProperty.property?.toApi() ?? null, - propertyId: formLeaseProperty.property?.id ?? 0, propertyName: formLeaseProperty.name ?? null, leaseArea: isNumber(formLeaseProperty.landArea) ? formLeaseProperty.landArea : 0, areaUnitType: isNumber(formLeaseProperty.landArea) ? toTypeCodeNullable(formLeaseProperty.areaUnitTypeCode) ?? null : null, - displayOrder: null, - location: null, // TODO: Add proper file location values when DB schema gets added ...getEmptyBaseAudit(formLeaseProperty.rowVersion), }; } diff --git a/source/frontend/src/features/mapSideBar/acquisition/add/models.ts b/source/frontend/src/features/mapSideBar/acquisition/add/models.ts index 89975cd409..4ac2677910 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/add/models.ts +++ b/source/frontend/src/features/mapSideBar/acquisition/add/models.ts @@ -4,7 +4,6 @@ import { ApiGen_Concepts_AcquisitionFile } from '@/models/api/generated/ApiGen_C import { ApiGen_Concepts_AcquisitionFileOwner } from '@/models/api/generated/ApiGen_Concepts_AcquisitionFileOwner'; import { ApiGen_Concepts_AcquisitionFileProperty } from '@/models/api/generated/ApiGen_Concepts_AcquisitionFileProperty'; import { getEmptyBaseAudit } from '@/models/defaultInitializers'; -import { latLngToApiLocation } from '@/utils'; import { fromTypeCode, stringToNumberOrNull, toTypeCodeNullable } from '@/utils/formUtils'; import { exists, isValidId, isValidIsoDateTime } from '@/utils/utils'; @@ -87,15 +86,9 @@ export class AcquisitionForm implements WithAcquisitionTeam, WithAcquisitionOwne } private toPropertyApi(x: PropertyForm): ApiGen_Concepts_AcquisitionFileProperty { + const apiFileProperty = x.toFilePropertyApi(this.id); return { - id: x.id ?? 0, - fileId: this.id ?? 0, - property: x.toApi(), - propertyId: x.apiId ?? 0, - propertyName: x.name ?? null, - location: latLngToApiLocation(x.fileLocation?.lat, x.fileLocation?.lng), - displayOrder: x.displayOrder ?? null, - rowVersion: x.rowVersion ?? null, + ...apiFileProperty, file: null, }; } diff --git a/source/frontend/src/features/mapSideBar/disposition/models/DispositionFormModel.ts b/source/frontend/src/features/mapSideBar/disposition/models/DispositionFormModel.ts index 5519190eba..f9a93948a8 100644 --- a/source/frontend/src/features/mapSideBar/disposition/models/DispositionFormModel.ts +++ b/source/frontend/src/features/mapSideBar/disposition/models/DispositionFormModel.ts @@ -2,7 +2,6 @@ import { IAutocompletePrediction } from '@/interfaces/IAutocomplete'; import { ApiGen_Concepts_DispositionFile } from '@/models/api/generated/ApiGen_Concepts_DispositionFile'; import { ApiGen_Concepts_DispositionFileProperty } from '@/models/api/generated/ApiGen_Concepts_DispositionFileProperty'; import { getEmptyBaseAudit } from '@/models/defaultInitializers'; -import { latLngToApiLocation } from '@/utils'; import { emptyStringtoNullable, fromTypeCode, toTypeCodeNullable } from '@/utils/formUtils'; import { exists, isValidIsoDateTime } from '@/utils/utils'; @@ -94,15 +93,9 @@ export class DispositionFormModel implements WithDispositionTeam { } private toPropertyApi(x: PropertyForm): ApiGen_Concepts_DispositionFileProperty { + const apiFileProperty = x.toFilePropertyApi(this.id); return { - id: x.id ?? 0, - fileId: this.id ?? 0, - property: x.toApi(), - propertyId: x.apiId ?? 0, - propertyName: x.name ?? null, - location: latLngToApiLocation(x.fileLocation?.lat, x.fileLocation?.lng), - displayOrder: x.displayOrder ?? null, - rowVersion: x.rowVersion ?? null, + ...apiFileProperty, file: null, }; } diff --git a/source/frontend/src/features/mapSideBar/property/tabs/propertyResearch/update/models.ts b/source/frontend/src/features/mapSideBar/property/tabs/propertyResearch/update/models.ts index 47ea175c7a..fbb3a23726 100644 --- a/source/frontend/src/features/mapSideBar/property/tabs/propertyResearch/update/models.ts +++ b/source/frontend/src/features/mapSideBar/property/tabs/propertyResearch/update/models.ts @@ -100,7 +100,7 @@ export class UpdatePropertyFormModel { documentReference: this.documentReference ?? null, researchSummary: this.researchSummary ?? null, property: null, - location: null, // TODO: Add proper file location values when DB schema gets added + location: null, fileId: this.researchFileId ?? 0, file: { ...getEmptyResearchFile(), rowVersion: this.researchFileRowVersion }, purposeTypes: this.purposeTypes?.map(x => x.toApi()) ?? null, diff --git a/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx b/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx index 33a62cffc3..079f5d9338 100644 --- a/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx +++ b/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx @@ -79,14 +79,13 @@ export const ResearchContainer: React.FunctionComponent const retrieved = await getResearchFile(props.researchFileId); if (exists(retrieved)) { const researchProperties = await getResearchFileProperties(props.researchFileId); - retrieved.fileProperties?.forEach(async fp => { - fp.property = researchProperties?.find(ap => fp.id === ap.id)?.property ?? null; - }); + retrieved.fileProperties = researchProperties ?? null; setFile({ ...retrieved, fileType: FileTypes.Research }); + setStaleFile(false); } else { setFile(undefined); } - }, [getResearchFile, getResearchFileProperties, props.researchFileId, setFile]); + }, [getResearchFile, getResearchFileProperties, props.researchFileId, setFile, setStaleFile]); const fetchLastUpdatedBy = useCallback(async () => { const retrieved = await getLastUpdatedBy(props.researchFileId); diff --git a/source/frontend/src/features/mapSideBar/research/add/models.ts b/source/frontend/src/features/mapSideBar/research/add/models.ts index dc89e21ed2..c21b7d5dfe 100644 --- a/source/frontend/src/features/mapSideBar/research/add/models.ts +++ b/source/frontend/src/features/mapSideBar/research/add/models.ts @@ -1,7 +1,6 @@ import { ApiGen_Concepts_ResearchFile } from '@/models/api/generated/ApiGen_Concepts_ResearchFile'; import { ApiGen_Concepts_ResearchFileProperty } from '@/models/api/generated/ApiGen_Concepts_ResearchFileProperty'; import { getEmptyBaseAudit } from '@/models/defaultInitializers'; -import { latLngToApiLocation } from '@/utils'; import { PropertyForm } from '../../shared/models'; import { ResearchFileProjectFormModel } from '../common/models'; @@ -45,23 +44,18 @@ export class ResearchForm { } private toPropertyApi(x: PropertyForm): ApiGen_Concepts_ResearchFileProperty { + const apiFileProperty = x.toFilePropertyApi(this.id); return { - id: x.id ?? 0, - property: x.toApi(), - propertyId: x.apiId ?? 0, - propertyName: x.name ?? null, - rowVersion: x.rowVersion ?? null, - displayOrder: x.displayOrder ?? null, - location: latLngToApiLocation(x.fileLocation?.lat, x.fileLocation?.lng), - documentReference: null, + ...apiFileProperty, file: null, - fileId: this.id ?? 0, + documentReference: null, isLegalOpinionObtained: null, isLegalOpinionRequired: null, purposeTypes: null, researchSummary: null, }; } + public static fromApi(model: ApiGen_Concepts_ResearchFile): ResearchForm { const newForm = new ResearchForm(); newForm.id = model.id; diff --git a/source/frontend/src/features/mapSideBar/shared/models.ts b/source/frontend/src/features/mapSideBar/shared/models.ts index e1c35d7ba8..8c515f8607 100644 --- a/source/frontend/src/features/mapSideBar/shared/models.ts +++ b/source/frontend/src/features/mapSideBar/shared/models.ts @@ -44,27 +44,13 @@ export class FileForm { return { id: this.id ?? 0, fileName: this.name, - fileProperties: this.properties.map(x => this.toPropertyApi(x)), + fileProperties: this.properties.map(x => x.toFilePropertyApi(this.id)), fileNumber: null, fileStatusTypeCode: null, ...getEmptyBaseAudit(this.rowVersion), }; } - private toPropertyApi(x: PropertyForm): ApiGen_Concepts_FileProperty { - return { - id: x.id ?? 0, - fileId: this.id ?? 0, - property: x.toApi(), - propertyId: x.apiId ?? 0, - propertyName: x.name ?? null, - location: latLngToApiLocation(x.fileLocation?.lat, x.fileLocation?.lng), - rowVersion: x.rowVersion ?? null, - displayOrder: null, - file: null, - }; - } - public static fromApi(model: ApiGen_Concepts_File): FileForm { const newForm = new FileForm(); newForm.id = model.id; @@ -299,6 +285,20 @@ export class PropertyForm { return newForm; } + public toFilePropertyApi(fileId?: number): ApiGen_Concepts_FileProperty { + return { + id: this.id ?? 0, + fileId: fileId ?? this.fileId ?? 0, + file: null, + property: this.toApi(), + propertyId: this.apiId ?? 0, + propertyName: this.name ?? null, + location: latLngToApiLocation(this.fileLocation?.lat, this.fileLocation?.lng), + displayOrder: this.displayOrder ?? null, + rowVersion: this.rowVersion ?? null, + }; + } + public toApi(): ApiGen_Concepts_Property { return { id: this.apiId ?? 0,