Skip to content

Commit

Permalink
psp-8771 & psp-8683 | Lease expiry using renewal information (#4189)
Browse files Browse the repository at this point in the history
* Updated lease expiry search to use renewal information. Updated expiry definition for leases

* Updated tests

* Fixed property management lease dates
  • Loading branch information
FuriousLlama authored Jul 13, 2024
1 parent 660e75a commit 8c468a8
Show file tree
Hide file tree
Showing 20 changed files with 417 additions and 237 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Pims.Core.Extensions;
using Pims.Dal.Entities;
using Pims.Dal.Entities.Models;
using Pims.Dal.Helpers.Extensions;
using Pims.Dal.Repositories;
using Pims.Dal.Security;
using Swashbuckle.AspNetCore.Annotations;
Expand Down Expand Up @@ -204,9 +205,13 @@ public IActionResult ExportLeasePayments(int fiscalYearStart)
public IEnumerable<LeaseModel> GetCrossJoinLeases(Lease.Models.Search.LeaseFilterModel filter, bool all = false)
{
var page = _leaseService.GetPage((LeaseFilter)filter, all);
var allLeases = page.Items.SelectMany(l => l.PimsLeasePeriods.DefaultIfEmpty(), (lease, period) => (lease, period))
var expiryDate = page.Items.Select(l => l.GetExpiryDate());

var allLeases = page.Items
.SelectMany(l => l.PimsLeasePeriods.DefaultIfEmpty(), (lease, period) => (lease, period))
.SelectMany(lt => lt.lease.PimsPropertyLeases.DefaultIfEmpty(), (leasePeriod, property) => (leasePeriod.period, leasePeriod.lease, property))
.SelectMany(ltp => ltp.lease.PimsLeaseTenants.DefaultIfEmpty(), (leasePeriodProperty, tenant) => (leasePeriodProperty.period, leasePeriodProperty.lease, leasePeriodProperty.property, tenant));
.SelectMany(ltp => ltp.lease.PimsLeaseTenants.DefaultIfEmpty(), (leasePeriodProperty, tenant) => (leasePeriodProperty.period, leasePeriodProperty.lease, leasePeriodProperty.property, tenant))
.SelectMany(ltpr => expiryDate, (leasePeriodPropertyTenant, expiryDate) => (leasePeriodPropertyTenant.period, leasePeriodPropertyTenant.lease, leasePeriodPropertyTenant.property, leasePeriodPropertyTenant.tenant, expiryDate));
var flatLeases = _mapper.Map<IEnumerable<LeaseModel>>(allLeases);
return flatLeases;
}
Expand Down
8 changes: 4 additions & 4 deletions source/backend/api/Areas/Reports/Mapping/Lease/LeaseMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ public class LeaseMap : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<(Entity.PimsLeasePeriod period, Entity.PimsLease lease, Entity.PimsPropertyLease property, Entity.PimsLeaseTenant tenant), Model.LeaseModel>()
config.NewConfig<(Entity.PimsLeasePeriod period, Entity.PimsLease lease, Entity.PimsPropertyLease property, Entity.PimsLeaseTenant tenant, DateTime? expiryDate), Model.LeaseModel>()
.AfterMapping((src, dest) =>
{
MapLease(src, dest);
});
}

private static void MapLease((Entity.PimsLeasePeriod period, Entity.PimsLease lease, Entity.PimsPropertyLease property, Entity.PimsLeaseTenant tenant) src, Model.LeaseModel dest)
private static void MapLease((Entity.PimsLeasePeriod period, Entity.PimsLease lease, Entity.PimsPropertyLease property, Entity.PimsLeaseTenant tenant, DateTime? expiryDate) src, Model.LeaseModel dest)
{
dest.LFileNo = src.lease.LFileNo;
dest.MotiRegion = src.lease.RegionCodeNavigation?.RegionName;
dest.StartDate = src.lease.OrigStartDate.FilterSqlMinDate().ToNullableDateOnly();
dest.EndDate = src.lease.OrigExpiryDate?.FilterSqlMinDate().ToNullableDateOnly();
dest.EndDate = src.expiryDate.ToNullableDateOnly();
dest.CurrentPeriodStartDate = src.lease.GetCurrentPeriodStartDate()?.FilterSqlMinDate().ToNullableDateOnly();
dest.CurrentTermEndDate = src.lease.GetCurrentPeriodEndDate()?.FilterSqlMinDate().ToNullableDateOnly();
dest.ProgramName = src.lease.LeaseProgramTypeCodeNavigation?.GetTypeDescriptionOther(src.lease.OtherLeaseProgramType);
Expand All @@ -36,7 +36,7 @@ private static void MapLease((Entity.PimsLeasePeriod period, Entity.PimsLease le
dest.InspectionNotes = src.lease.InspectionNotes;
dest.InspectionDate = src.lease.InspectionDate?.FilterSqlMinDate();
dest.LeaseNotes = src.lease.LeaseNotes;
dest.IsExpired = (src.lease.GetExpiryDate() < DateTime.Now).BoolToYesNo();
dest.IsExpired = (src.expiryDate < DateTime.Now).BoolToYesNo();
dest.LeaseAmount = src.period?.PaymentAmount;
dest.PeriodStartDate = src.period?.PeriodStartDate.FilterSqlMinDate().ToNullableDateOnly();
dest.PeriodExpiryDate = src.period?.PeriodExpiryDate?.FilterSqlMinDate().ToNullableDateOnly();
Expand Down
25 changes: 17 additions & 8 deletions source/backend/dal/Repositories/LeaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ public IQueryable<PimsLease> GenerateLeaseQuery(LeaseFilter filter, HashSet<shor
.ThenInclude(p => p.Address)
.Include(l => l.PimsPropertyLeases)
.ThenInclude(p => p.AreaUnitTypeCodeNavigation)
.Include(pl => pl.PimsPropertyLeases)
.Include(l => l.PimsPropertyLeases)
.ThenInclude(p => p.Property)
.ThenInclude(n => n.PimsHistoricalFileNumbers)
.ThenInclude(t => t.HistoricalFileNumberTypeCodeNavigation)
Expand All @@ -908,8 +908,9 @@ public IQueryable<PimsLease> GenerateLeaseQuery(LeaseFilter filter, HashSet<shor
.ThenInclude(t => t.Person)
.Include(l => l.PimsLeaseTenants)
.ThenInclude(t => t.Organization)
.Include(p => p.RegionCodeNavigation)
.Include(l => l.RegionCodeNavigation)
.Include(l => l.PimsLeasePeriods)
.Include(l => l.PimsLeaseRenewals)
.AsNoTracking();

if (loadPayments)
Expand Down Expand Up @@ -1100,17 +1101,25 @@ private static ExpressionStarter<PimsLease> GenerateCommonLeaseQuery(LeaseFilter

var expiryStartDate = filter.ExpiryStartDate.ToNullableDateTime();
var expiryEndDate = filter.ExpiryEndDate.ToNullableDateTime();
if (filter.ExpiryStartDate != null && filter.ExpiryEndDate != null)
if (expiryStartDate != null && expiryEndDate != null)
{
predicateBuilder.And(l => l.OrigExpiryDate >= expiryStartDate && l.OrigExpiryDate <= expiryEndDate);
predicateBuilder = predicateBuilder.And(l => l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max().HasValue ?
l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max() >= expiryStartDate &&
l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max() <= expiryEndDate :
l.OrigExpiryDate >= expiryStartDate &&
l.OrigExpiryDate <= expiryEndDate);
}
else if (filter.ExpiryStartDate != null)
else if (expiryStartDate != null)
{
predicateBuilder = predicateBuilder.And(l => l.OrigExpiryDate >= expiryStartDate);
predicateBuilder = predicateBuilder.And(l => l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max().HasValue ?
l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max() >= expiryStartDate :
l.OrigExpiryDate >= expiryStartDate);
}
else if (filter.ExpiryEndDate != null)
else if (expiryEndDate != null)
{
predicateBuilder = predicateBuilder.And(l => l.OrigExpiryDate <= expiryEndDate);
predicateBuilder = predicateBuilder.And(l => l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max().HasValue ?
l.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(rf => rf.ExpiryDt).Max() <= expiryEndDate :
l.OrigExpiryDate <= expiryEndDate);
}

if (filter.RegionType.HasValue)
Expand Down
2 changes: 2 additions & 0 deletions source/backend/dal/Repositories/PropertyLeaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public IEnumerable<PimsPropertyLease> GetAllByPropertyId(long propertyId)
.ThenInclude(l => l.LeaseStatusTypeCodeNavigation)
.Include(pl => pl.Lease)
.ThenInclude(l => l.PimsLeasePeriods)
.Include(pl => pl.Lease)
.ThenInclude(l => l.PimsLeaseRenewals)
.Where(p => p.PropertyId == propertyId);
}

Expand Down
15 changes: 2 additions & 13 deletions source/backend/entities/Extensions/LeaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,8 @@ public static string GetTenantName(this Pims.Dal.Entities.PimsLeaseTenant lease)
/// <returns></returns>
public static DateTime? GetExpiryDate(this Pims.Dal.Entities.PimsLease lease)
{
if (lease.PimsLeasePeriods != null && lease.PimsLeasePeriods.Any(p => p.PeriodExpiryDate == null && !p.IsFlexibleDuration))
{
return null;
}
if (lease.OrigExpiryDate != null)
{
if (lease.PimsLeasePeriods != null && lease.PimsLeasePeriods.Any(p => p.PeriodExpiryDate > lease.OrigExpiryDate && !p.IsFlexibleDuration))
{
return lease.PimsLeasePeriods.OrderByDescending(p => p.PeriodExpiryDate).FirstOrDefault().PeriodExpiryDate;
}
return lease.OrigExpiryDate;
}
return lease.PimsLeasePeriods?.OrderByDescending(p => p.PeriodExpiryDate).FirstOrDefault(p => !p.IsFlexibleDuration)?.PeriodExpiryDate;
var expiryDate = lease.PimsLeaseRenewals.Where(r => r.IsExercised == true).Select(fr => fr.ExpiryDt).Append(lease.OrigExpiryDate).Max();
return expiryDate;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,31 @@ public void GetExpiryDate_OrigExpiry()
}

[Fact]
public void GetExpiryDate_TermExpiry()
public void GetExpiryDate_RenewalExpiry()
{
DateTime now = DateTime.Now;
PimsLease lease = new PimsLease()
{
OrigExpiryDate = null,
PimsLeasePeriods = new List<PimsLeasePeriod>() {
new PimsLeasePeriod() { PeriodExpiryDate = now }, },
PimsLeaseRenewals = new List<PimsLeaseRenewal>() {
new PimsLeaseRenewal() { IsExercised = true, ExpiryDt = now }, },
};
Assert.Equal(now, lease.GetExpiryDate());
}

[Fact]
public void GetExpiryDate_RenewalExpiry_NotExercised()
{
DateTime now = DateTime.Now;
PimsLease lease = new PimsLease()
{
OrigExpiryDate = null,
PimsLeaseRenewals = new List<PimsLeaseRenewal>() {
new PimsLeaseRenewal() { IsExercised = false, ExpiryDt = now }, },
};
Assert.Equal(null, lease.GetExpiryDate());
}

[Fact]
public void GetExpiryDate_OrigExpiryLater()
{
Expand All @@ -50,56 +63,56 @@ public void GetExpiryDate_OrigExpiryLater()
PimsLease lease = new PimsLease()
{
OrigExpiryDate = later,
PimsLeasePeriods = new List<PimsLeasePeriod>() {
new PimsLeasePeriod() { PeriodExpiryDate = now }, },
PimsLeaseRenewals = new List<PimsLeaseRenewal>() {
new PimsLeaseRenewal() { IsExercised=true, ExpiryDt = now }, },
};
Assert.Equal(later, lease.GetExpiryDate());
}

[Fact]
public void GetExpiryDate_TermExpiryLater()
public void GetExpiryDate_RenewalExpiryLater()
{
DateTime now = DateTime.Now;
DateTime later = now.AddDays(1);
PimsLease lease = new PimsLease()
{
OrigExpiryDate = now,
PimsLeasePeriods =
new List<PimsLeasePeriod>() { new PimsLeasePeriod() { PeriodExpiryDate = later } },
PimsLeaseRenewals =
new List<PimsLeaseRenewal>() { new PimsLeaseRenewal() { IsExercised = true, ExpiryDt = later } },
};
Assert.Equal(later, lease.GetExpiryDate());
}

[Fact]
public void GetExpiryDate_MultipleTermExpiryLater()
public void GetExpiryDate_MultipleRenewalExpiryLater()
{
DateTime now = DateTime.Now;
DateTime later = now.AddDays(1);
DateTime before = now.AddDays(-1);
PimsLease lease = new PimsLease()
{
OrigExpiryDate = now,
PimsLeasePeriods =
new List<PimsLeasePeriod>() { new PimsLeasePeriod() { PeriodExpiryDate = later },
new PimsLeasePeriod() { PeriodExpiryDate = before }, },
PimsLeaseRenewals =
new List<PimsLeaseRenewal>() { new PimsLeaseRenewal() { IsExercised=true, ExpiryDt = later },
new PimsLeaseRenewal() { IsExercised=true, ExpiryDt = before }, },
};
Assert.Equal(later, lease.GetExpiryDate());
}

[Fact]
public void GetExpiryDate_TermNoExpiry()
public void GetExpiryDate_RenewalNoExpiry()
{
DateTime now = DateTime.Now;
DateTime later = now.AddDays(1);
DateTime before = now.AddDays(-1);
PimsLease lease = new PimsLease()
{
OrigExpiryDate = now,
PimsLeasePeriods =
new List<PimsLeasePeriod>() { new PimsLeasePeriod() { PeriodExpiryDate = null },
new PimsLeasePeriod() { PeriodExpiryDate = before }, },
PimsLeaseRenewals =
new List<PimsLeaseRenewal>() { new PimsLeaseRenewal() { IsExercised=true, ExpiryDt = null },
new PimsLeaseRenewal() { IsExercised = false, ExpiryDt = before }, },
};
Assert.Equal(null, lease.GetExpiryDate());
Assert.Equal(now, lease.GetExpiryDate());
}
#endregion
}
Expand Down
19 changes: 19 additions & 0 deletions source/frontend/src/features/leases/leaseUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import moment from 'moment';

import { ApiGen_Concepts_Lease } from '@/models/api/generated/ApiGen_Concepts_Lease';
import { ApiGen_Concepts_LeaseRenewal } from '@/models/api/generated/ApiGen_Concepts_LeaseRenewal';
import { ApiGen_Concepts_LeaseTenant } from '@/models/api/generated/ApiGen_Concepts_LeaseTenant';
import { isValidId } from '@/utils';
import { formatNames } from '@/utils/personUtils';
Expand All @@ -16,6 +20,21 @@ export const getAllNames = (tenants: ApiGen_Concepts_LeaseTenant[]): string[] =>
return allNames;
};

export const getCalculatedExpiry = (
lease: ApiGen_Concepts_Lease,
renewals: ApiGen_Concepts_LeaseRenewal[],
): string => {
const excercisedRenewalDates = renewals.filter(r => r.isExercised).flatMap(fr => fr.expiryDt);

let calculatedExpiry: string | null = lease?.expiryDate ?? '';
for (let i = 0; i < excercisedRenewalDates.length; i++) {
if (moment(excercisedRenewalDates[i]).isAfter(calculatedExpiry)) {
calculatedExpiry = excercisedRenewalDates[i];
}
}
return calculatedExpiry;
};

export const getSuggestedFee = (isPublicBenefit: boolean, isFinancialGain: boolean): string => {
if (isPublicBenefit == null || isFinancialGain == null) return 'Unknown';
else if (isPublicBenefit && isFinancialGain) return 'Licence Administration Fee (LAF) *';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ResetButton, SearchButton } from '@/components/common/buttons';
import { FastDatePicker, Input } from '@/components/common/form';
import { UserRegionSelectContainer } from '@/components/common/form/UserRegionSelect/UserRegionSelectContainer';
import { SelectInput } from '@/components/common/List/SelectInput';
import { SectionField } from '@/components/common/Section/SectionField';
import { FilterBoxForm } from '@/components/common/styles';
import TooltipIcon from '@/components/common/TooltipIcon';
import { LEASE_PROGRAM_TYPES, LEASE_STATUS_TYPES } from '@/constants/API';
Expand Down Expand Up @@ -107,9 +108,9 @@ export const LeaseFilter: React.FunctionComponent<React.PropsWithChildren<ILease
{formikProps => (
<FilterBoxForm className="p-3">
<Row>
<Col xl="6">
<Col xs="6">
<Row>
<Col xl="auto">
<Col xs="auto">
<strong>Search by:</strong>
</Col>
<Col>
Expand Down Expand Up @@ -206,48 +207,45 @@ export const LeaseFilter: React.FunctionComponent<React.PropsWithChildren<ILease
</Col>
</Row>
</Col>
<Col xl="5">
<Row>
<Col xl="auto">
<strong>Expiry date:</strong>
</Col>
<Col>
<Row>
<Col>
<FastDatePicker
field="expiryStartDate"
formikProps={formikProps}
placeholderText="from date"
/>
</Col>
<Col>
<FastDatePicker
field="expiryEndDate"
formikProps={formikProps}
placeholderText="to date"
/>
</Col>
</Row>
<Row>
<Col>
<UserRegionSelectContainer field="regionType" placeholder="All Regions" />
</Col>
<Col>
<Row>
<Col xs="10" className="pr-0 mr-0">
<Input field="details" placeholder="Keyword" />
</Col>
<Col xs="1" className="pl-0 ml-0">
<TooltipIcon
toolTipId="lease-search-keyword-tooltip"
toolTip="Search 'Lease description' and 'Notes' fields"
/>
</Col>
</Row>
</Col>
</Row>
</Col>
</Row>
<Col xs="5">
<SectionField label="Expiry date" labelWidth="2">
<Row>
<Col>
<FastDatePicker
field="expiryStartDate"
formikProps={formikProps}
placeholderText="from date"
/>
</Col>
<Col>
<FastDatePicker
field="expiryEndDate"
formikProps={formikProps}
placeholderText="to date"
/>
</Col>
</Row>
</SectionField>
<SectionField label="" labelWidth="2">
<Row>
<Col>
<UserRegionSelectContainer field="regionType" placeholder="All Regions" />
</Col>
<Col>
<Row noGutters>
<Col xs="9">
<Input field="details" placeholder="Keyword" />
</Col>
<Col xs="1">
<TooltipIcon
toolTipId="lease-search-keyword-tooltip"
toolTip="Search 'Lease description' and 'Notes' fields"
/>
</Col>
</Row>
</Col>
</Row>
</SectionField>
</Col>
<ColButtons xl="1">
<Row>
Expand Down
Loading

0 comments on commit 8c468a8

Please sign in to comment.