Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSP-8977 : Add Total Allowable Compensation to Compensation Section #4280

Merged
merged 7 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions source/backend/api/Services/CompReqFinancialService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ public IEnumerable<PimsCompReqFinancial> GetAllByAcquisitionFileId(long acquisit
return _compReqFinancialRepository.GetAllByAcquisitionFileId(acquisitionFileId, finalOnly);
}

public IEnumerable<PimsCompReqFinancial> GetAllByLeaseFileId(long leaseFileId, bool? finalOnly)
{
_logger.LogInformation("Getting pims comp req financials by {leaseFileId}", leaseFileId);
_user.ThrowIfNotAuthorized(Permissions.CompensationRequisitionView);
_user.ThrowIfNotAuthorized(Permissions.LeaseView);

return _compReqFinancialRepository.GetAllByLeaseFileId(leaseFileId, finalOnly);
}

public IEnumerable<PimsCompReqFinancial> SearchCompensationRequisitionFinancials(AcquisitionReportFilterModel filter)
{
_logger.LogInformation("Searching all comp req financials matching the filter: {filter}", filter);
Expand Down
19 changes: 19 additions & 0 deletions source/backend/api/Services/CompensationRequisitionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ private PimsCompensationRequisition UpdateLeaseFileCompensation(PimsCompensation

var currentCompensation = _compensationRequisitionRepository.GetById(compensationRequisition.CompensationRequisitionId);

var currentLeaseFile = _leaseRepository.Get((long)currentCompensation.LeaseId);
CheckTotalAllowableCompensation(currentLeaseFile, compensationRequisition);
compensationRequisition.FinalizedDate = GetFinalizedDate(currentCompensation.IsDraft, compensationRequisition.IsDraft, currentCompensation.FinalizedDate);

PimsCompensationRequisition updatedEntity = _compensationRequisitionRepository.Update(compensationRequisition);

AddLeaseNoteIfStatusChanged(compensationRequisition.Internal_Id, (long)compensationRequisition.LeaseId, currentCompensation.IsDraft, compensationRequisition.IsDraft);
Expand Down Expand Up @@ -344,6 +347,22 @@ private void CheckTotalAllowableCompensation(PimsAcquisitionFile currentAcquisit
}
}

private void CheckTotalAllowableCompensation(PimsLease currentLeaseFile, PimsCompensationRequisition newCompensation)
{
if (!currentLeaseFile.TotalAllowableCompensation.HasValue || (newCompensation.IsDraft.HasValue && newCompensation.IsDraft.Value))
{
return;
}

IEnumerable<PimsCompReqFinancial> allFinancialsForFile = _compReqFinancialService.GetAllByLeaseFileId(currentLeaseFile.LeaseId, true);
IEnumerable<PimsCompReqFinancial> allUnchangedFinancialsForFile = allFinancialsForFile.Where(f => f.CompensationRequisitionId != newCompensation.CompensationRequisitionId);
decimal newTotalCompensation = allUnchangedFinancialsForFile.Concat(newCompensation.PimsCompReqFinancials).Aggregate(0m, (acc, f) => acc + (f.TotalAmt ?? 0m));
if (newTotalCompensation > currentLeaseFile.TotalAllowableCompensation)
{
throw new BusinessRuleViolationException("Your compensation requisition cannot be saved in FINAL status, as its compensation amount exceeds total allowable compensation for this file.");
}
}

private AcquisitionStatusTypes GetCurrentAcquisitionStatus(long acquisitionFileId)
{
var currentAcquisitionFile = _acqFileRepository.GetById(acquisitionFileId);
Expand Down
2 changes: 2 additions & 0 deletions source/backend/api/Services/ICompReqFinancialService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public interface ICompReqFinancialService
{
IEnumerable<PimsCompReqFinancial> GetAllByAcquisitionFileId(long acquisitionFileId, bool? finalOnly);

IEnumerable<PimsCompReqFinancial> GetAllByLeaseFileId(long leaseFileId, bool? finalOnly);

IEnumerable<PimsCompReqFinancial> SearchCompensationRequisitionFinancials(AcquisitionReportFilterModel filter);
}
}
24 changes: 23 additions & 1 deletion source/backend/api/Services/LeaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class LeaseService : BaseService, ILeaseService
private readonly IUserRepository _userRepository;
private readonly IPropertyService _propertyService;
private readonly ILookupRepository _lookupRepository;
private readonly ICompReqFinancialService _compReqFinancialService;

public LeaseService(
ClaimsPrincipal user,
Expand All @@ -50,7 +51,8 @@ public LeaseService(
ILeaseRenewalRepository renewalRepository,
IUserRepository userRepository,
IPropertyService propertyService,
ILookupRepository lookupRepository)
ILookupRepository lookupRepository,
ICompReqFinancialService compReqFinancialService)
: base(user, logger)
{
_logger = logger;
Expand All @@ -66,6 +68,7 @@ public LeaseService(
_userRepository = userRepository;
_propertyService = propertyService;
_lookupRepository = lookupRepository;
_compReqFinancialService = compReqFinancialService;
}

public PimsLease GetById(long leaseId)
Expand Down Expand Up @@ -226,6 +229,8 @@ public PimsLease Update(PimsLease lease, IEnumerable<UserOverrideCode> userOverr

ValidateRenewalDates(lease, lease.PimsLeaseRenewals);

ValidateNewTotalAllowableCompensation(lease.LeaseId, lease.TotalAllowableCompensation);

_leaseRepository.Update(lease, false);
var leaseWithProperties = AssociatePropertyLeases(lease, userOverrides);

Expand Down Expand Up @@ -270,6 +275,7 @@ public PimsLease Update(PimsLease lease, IEnumerable<UserOverrideCode> userOverr
}

_leaseRepository.CommitTransaction();

return _leaseRepository.GetNoTracking(lease.LeaseId);
}

Expand Down Expand Up @@ -402,6 +408,22 @@ private static void ValidateRenewalDates(PimsLease lease, ICollection<PimsLeaseR
}
}

private void ValidateNewTotalAllowableCompensation(long currentLeaseFileId, decimal? newAllowableCompensation)
{
if (newAllowableCompensation is null)
{
return;
}

IEnumerable<PimsCompReqFinancial> allFinalFinancialsOnFile = _compReqFinancialService.GetAllByLeaseFileId(currentLeaseFileId, true);
var currentActualCompensation = allFinalFinancialsOnFile.Aggregate(0m, (acc, f) => acc + (f.TotalAmt ?? 0m));
if (newAllowableCompensation < currentActualCompensation)
{
throw new BusinessRuleViolationException("The Total Allowable Compensation value cannot be saved because the value provided is less than current sum of the final compensation requisitions in this file. " +
"\n\nTo continue, adjust the value to accommodate the existing compensation requisitions in the file or contact your system administrator to adjust final compensations.");
}
}

private PimsLeaseNote GeneratePimsLeaseNote(PimsLease currentLease, PimsLease lease)
{
var leaseStatuses = _lookupRepository.GetAllLeaseStatusTypes();
Expand Down
2 changes: 2 additions & 0 deletions source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.CancellationReason, src => src.CancellationReason)
.Map(dest => dest.TerminationReason, src => src.TerminationReason)
.Map(dest => dest.Project, src => src.Project)
.Map(dest => dest.TotalAllowableCompensation, src => src.TotalAllowableCompensation)
.Map(dest => dest.Stakeholders, src => src.PimsLeaseStakeholders)
.Map(dest => dest.FileChecklistItems, src => src.PimsLeaseChecklistItems)
.Map(dest => dest.PrimaryArbitrationCity, src => src.PrimaryArbitrationCity)
Expand Down Expand Up @@ -100,6 +101,7 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.PimsLeaseChecklistItems, src => src.FileChecklistItems)
.Map(dest => dest.PrimaryArbitrationCity, src => src.PrimaryArbitrationCity)
.Map(dest => dest.ProjectId, src => src.Project != null ? src.Project.Id : (long?)null)
.Map(dest => dest.TotalAllowableCompensation, src => src.TotalAllowableCompensation)
.Map(dest => dest.IsPublicBenefit, src => src.IsPublicBenefit)
.Map(dest => dest.IsFinancialGain, src => src.IsFinancialGain)
.Map(dest => dest.FeeDeterminationNote, src => src.FeeDeterminationNote)
Expand Down
17 changes: 17 additions & 0 deletions source/backend/dal/Repositories/CompReqFinancialRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,26 @@ public IEnumerable<PimsCompReqFinancial> GetAllByAcquisitionFileId(long acquisit
{
query = query.Where(c => c.CompensationRequisition.IsDraft == false);
}

return query.AsNoTracking().ToArray();
}

public IEnumerable<PimsCompReqFinancial> GetAllByLeaseFileId(long leaseFileId, bool? finalOnly)
{
this._user.ThrowIfNotAllAuthorized(Security.Permissions.CompensationRequisitionView);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you just use ThrowIfNotAuthorized here (there is only one permission).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated


var query = Context.PimsCompReqFinancials
.Include(c => c.CompensationRequisition)
.Where(c => c.CompensationRequisition.LeaseId == leaseFileId);

if (finalOnly == true)
{
query = query.Where(c => c.CompensationRequisition.IsDraft == false);
}

return query.AsNoTracking().ToList();
}

public IEnumerable<PimsCompReqFinancial> SearchCompensationRequisitionFinancials(AcquisitionReportFilterModel filter)
{
using var scope = Logger.QueryScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public interface ICompReqFinancialRepository : IRepository<PimsCompReqFinancial>
{
IEnumerable<PimsCompReqFinancial> GetAllByAcquisitionFileId(long acquisitionFileId, bool? finalOnly);

IEnumerable<PimsCompReqFinancial> GetAllByLeaseFileId(long leaseFileId, bool? finalOnly);

IEnumerable<PimsCompReqFinancial> SearchCompensationRequisitionFinancials(AcquisitionReportFilterModel filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,6 @@ public void Update_Status_BackToDraft_NoPermission()
act.Should().Throw<BusinessRuleViolationException>();
}


[Fact]
public void Update_Status_BackToNull_NoPermission()
{
Expand Down Expand Up @@ -819,6 +818,88 @@ public void Update_Fail_ValidMultipleTotalAllowableCompensation()
act.Should().Throw<BusinessRuleViolationException>();
}

[Fact]
public void Update_Lease_Fail_ValidMultipleTotalAllowableCompensation()
{
// Arrange
var service = this.CreateCompRequisitionServiceWithPermissions(Permissions.CompensationRequisitionEdit);
var compReqFinancialsService = this._helper.GetService<Mock<ICompReqFinancialService>>();

var compRepository = this._helper.GetService<Mock<ICompensationRequisitionRepository>>();
var leaseRepository = this._helper.GetService<Mock<ILeaseRepository>>();

compRepository.Setup(x => x.Update(It.IsAny<PimsCompensationRequisition>()))
.Returns(new PimsCompensationRequisition { Internal_Id = 1, LeaseId = 1, IsDraft = true }); ;
compRepository.Setup(x => x.GetById(It.IsAny<long>()))
.Returns(new PimsCompensationRequisition { Internal_Id = 1, LeaseId = 1, IsDraft = null });

compReqFinancialsService.Setup(x => x.GetAllByLeaseFileId(It.IsAny<long>(), true)).Returns(
new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { CompensationRequisitionId = 1, TotalAmt = 1000 },
new PimsCompReqFinancial() { CompensationRequisitionId = 2, TotalAmt = 100 }, });

leaseRepository.Setup(x => x.Get(It.IsAny<long>())).Returns(new PimsLease()
{
TotalAllowableCompensation = 299,
PimsCompensationRequisitions = new List<PimsCompensationRequisition>() { new PimsCompensationRequisition() { CompensationRequisitionId = 1,
PimsCompReqFinancials = new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 100 } } }, },
LeaseStatusTypeCode = LeaseStatusTypes.ACTIVE.ToString()
});

// Act
Action act = () => service.Update(FileTypes.Lease, new PimsCompensationRequisition()
{
Internal_Id = 1,
LeaseId = 1,
ConcurrencyControlNumber = 2,
IsDraft = false,
PimsCompReqFinancials = new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 200 } },
});

//Assert
act.Should().Throw<BusinessRuleViolationException>();
}

[Fact]
public void Update_Lease_Fail_TotalAllowableExceeded()
{
// Arrange
var service = this.CreateCompRequisitionServiceWithPermissions(Permissions.CompensationRequisitionEdit);
var compReqFinancialsService = this._helper.GetService<Mock<ICompReqFinancialService>>();

var compRepository = this._helper.GetService<Mock<ICompensationRequisitionRepository>>();
var leaseRepository = this._helper.GetService<Mock<ILeaseRepository>>();

compRepository.Setup(x => x.Update(It.IsAny<PimsCompensationRequisition>()))
.Returns(new PimsCompensationRequisition { Internal_Id = 1, LeaseId = 1, IsDraft = true }); ;
compRepository.Setup(x => x.GetById(It.IsAny<long>()))
.Returns(new PimsCompensationRequisition { Internal_Id = 1, LeaseId = 1, IsDraft = null });

compReqFinancialsService.Setup(x => x.GetAllByLeaseFileId(It.IsAny<long>(), true)).Returns(
new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { CompensationRequisitionId = 1, TotalAmt = 1000 },
new PimsCompReqFinancial() { CompensationRequisitionId = 2, TotalAmt = 100 }, });

leaseRepository.Setup(x => x.Get(It.IsAny<long>())).Returns(new PimsLease()
{
TotalAllowableCompensation = 99,
PimsCompensationRequisitions = new List<PimsCompensationRequisition>() { new PimsCompensationRequisition() { CompensationRequisitionId = 1,
PimsCompReqFinancials = new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 100 } } }, },
LeaseStatusTypeCode = LeaseStatusTypes.ACTIVE.ToString()
});

// Act
Action act = () => service.Update(FileTypes.Lease, new PimsCompensationRequisition()
{
Internal_Id = 1,
LeaseId = 1,
ConcurrencyControlNumber = 2,
IsDraft = false,
PimsCompReqFinancials = new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 200 } },
});

//Assert
act.Should().Throw<BusinessRuleViolationException>();
}

[Fact]
public void Delete_NoPermission()
{
Expand Down
56 changes: 56 additions & 0 deletions source/backend/tests/unit/api/Services/LeaseServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Pims.Api.Services;
using Pims.Core.Exceptions;
using Pims.Core.Test;
using Pims.Dal;
using Pims.Dal.Entities;
using Pims.Dal.Exceptions;
using Pims.Dal.Repositories;
Expand Down Expand Up @@ -299,6 +300,61 @@ public void Update_NoPermission()
leaseRepository.Verify(x => x.Update(It.IsAny<PimsLease>(), It.IsAny<bool>()), Times.Never);
}

[Fact]
public void Update_NewTotalAllowableCompensation_Success()
{
// Arrange
var service = this.CreateLeaseService(Permissions.LeaseEdit);

var currentLeaseEntity = EntityHelper.CreateLease(1, addProperty: false);

var leaseRepository = this._helper.GetService<Mock<ILeaseRepository>>();
var userRepository = this._helper.GetService<Mock<IUserRepository>>();
var compReqFinancialRepository = this._helper.GetService<Mock<ICompReqFinancialService>>();

leaseRepository.Setup(x => x.GetNoTracking(It.IsAny<long>())).Returns(currentLeaseEntity);
leaseRepository.Setup(x => x.Get(It.IsAny<long>())).Returns(currentLeaseEntity);

userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny<Guid>())).Returns(EntityHelper.CreateUser("Test"));
compReqFinancialRepository.Setup(c => c.GetAllByLeaseFileId(It.IsAny<long>(), true)).Returns(
new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 50 } });

// Act
currentLeaseEntity.TotalAllowableCompensation = 100;
var result = service.Update(currentLeaseEntity, new List<UserOverrideCode>());

// Assert
result.Should().NotBeNull();
result.TotalAllowableCompensation.Equals(100);
}

[Fact]
public void Update_NewTotalAllowableCompensation_Failure_LessThenCurrentFinancials()
{
// Arrange
var service = this.CreateLeaseService(Permissions.LeaseEdit);

var currentLeaseEntity = EntityHelper.CreateLease(1, addProperty: false);

var leaseRepository = this._helper.GetService<Mock<ILeaseRepository>>();
var userRepository = this._helper.GetService<Mock<IUserRepository>>();
var compReqFinancialRepository = this._helper.GetService<Mock<ICompReqFinancialService>>();

leaseRepository.Setup(x => x.GetNoTracking(It.IsAny<long>())).Returns(currentLeaseEntity);
leaseRepository.Setup(x => x.Get(It.IsAny<long>())).Returns(currentLeaseEntity);

userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny<Guid>())).Returns(EntityHelper.CreateUser("Test"));
compReqFinancialRepository.Setup(c => c.GetAllByLeaseFileId(It.IsAny<long>(), true)).Returns(
new List<PimsCompReqFinancial>() { new PimsCompReqFinancial() { TotalAmt = 100 } });

// Act
currentLeaseEntity.TotalAllowableCompensation = 99;
Action act = () => service.Update(currentLeaseEntity, new List<UserOverrideCode>());

// Assert
act.Should().Throw<BusinessRuleViolationException>();
}

[Fact]
public void Update_Without_StatusNote()
{
Expand Down
Loading
Loading