Skip to content

Commit

Permalink
psp-7045 add lease advanced filter.
Browse files Browse the repository at this point in the history
  • Loading branch information
devinleighsmith committed Nov 1, 2023
1 parent 22874b1 commit 93697c0
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 1 deletion.
5 changes: 5 additions & 0 deletions source/backend/dal/Models/PropertyFilterCriteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public class PropertyFilterCriteria
/// </summary>
public string LeaseStatus { get; set; }

/// <summary>
/// get/set - The lease receivable/payable type to filter by.
/// </summary>
public string LeasePayRcvblType { get; set; }

/// <summary>
/// get/set - The multiple lease types to filter by.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions source/backend/dal/Repositories/PropertyRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,12 @@ public HashSet<long> GetMatchingIds(PropertyFilterCriteria filter)
p.PimsPropertyLeases.Any(pl => filter.LeasePurposes.Contains(pl.Lease.LeasePurposeTypeCode)));
}

if (!string.IsNullOrEmpty(filter.LeasePayRcvblType))
{
query = query.Where(p =>
p.PimsPropertyLeases.Any(pl => pl.Lease.LeasePayRvblTypeCode == filter.LeasePayRcvblType || filter.LeasePayRcvblType == "all"));
}

// Anomalies
if (filter.AnomalyIds != null && filter.AnomalyIds.Count > 0)
{
Expand Down
159 changes: 159 additions & 0 deletions source/backend/tests/unit/dal/Repositories/PropertyRepositoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,165 @@ public void GetById_Success()
}
#endregion

#region GetMatchingIds
[Fact]
public void GetMatchingIds_LeaseRcbvl_All_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
var lease = EntityHelper.CreateLease(1, addProperty:false);
property.PimsPropertyLeases.Add(new PimsPropertyLease() { PropertyId = property.Internal_Id, LeaseId = lease.Internal_Id, Lease = lease });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { LeasePayRcvblType = "all" });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_LeaseStatus_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
var lease = EntityHelper.CreateLease(1, pimsLeaseStatusType: new PimsLeaseStatusType() { Id = "test2" }, addProperty: false);
property.PimsPropertyLeases.Add(new PimsPropertyLease() { PropertyId = property.Internal_Id, LeaseId = lease.Internal_Id, Lease = lease });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { LeaseStatus = "test2" });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_LeaseType_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
var lease = EntityHelper.CreateLease(1, pimsLeaseLicenseType: new PimsLeaseLicenseType() { Id = "test" }, addProperty: false);
property.PimsPropertyLeases.Add(new PimsPropertyLease() { PropertyId = property.Internal_Id, LeaseId = lease.Internal_Id, Lease = lease });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { LeaseTypes = new List<string>() { "test" } });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_LeasePurpose_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
var lease = EntityHelper.CreateLease(1, pimsLeasePurposeType: new PimsLeasePurposeType() { Id = "test" }, addProperty: false);
property.PimsPropertyLeases.Add(new PimsPropertyLease() { PropertyId = property.Internal_Id, LeaseId = lease.Internal_Id, Lease = lease });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { LeasePurposes = new List<string>() { "test" } });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_Anomaly_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
property.PimsPropPropAnomalyTypes.Add(new PimsPropPropAnomalyType() { PropertyAnomalyTypeCode = "test" });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { AnomalyIds = new List<string>() { "test" } });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_Project_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
property.PimsPropertyAcquisitionFiles.Add(new PimsPropertyAcquisitionFile() { AcquisitionFile = new PimsAcquisitionFile() { ProjectId = 1 } });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { ProjectId = 1 });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_Tenure_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
property.PimsPropPropTenureTypes.Add(new PimsPropPropTenureType() { PropertyTenureTypeCode = "test" });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { TenureStatuses = new List<string>() { "test" } });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_TenureRoad_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
property.PimsPropPropRoadTypes.Add(new PimsPropPropRoadType() { PropertyRoadTypeCode = "test" });
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { TenureRoadTypes = new List<string>() { "test" } });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}

[Fact]
public void GetMatchingIds_TenurePph_Success()
{
// Arrange
var repository = CreateRepositoryWithPermissions(Permissions.PropertyView);
var property = EntityHelper.CreateProperty(100);
property.PphStatusTypeCode = "test";
_helper.AddAndSaveChanges(property);

// Act
var result = repository.GetMatchingIds(new PropertyFilterCriteria() { TenurePPH = "test" });

// Assert
result.Should().NotBeNull();
result.Should().HaveCount(1);
}
#endregion

#region GetByPid
[Fact]
public void GetByPid_Success()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { FormikProps } from 'formik';
import { createMemoryHistory } from 'history';
import { forwardRef } from 'react';

import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext';
import { mockLookups } from '@/mocks/lookups.mock';
import { mapMachineBaseMock } from '@/mocks/mapFSM.mock';
import { getMockApiPropertyManagement } from '@/mocks/propertyManagement.mock';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { render, RenderOptions, waitFor } from '@/utils/test-utils';

import { FilterContentContainer, IFilterContentContainerProps } from './FilterContentContainer';
import { IFilterContentFormProps } from './FilterContentForm';
import { PropertyFilterFormModel } from './models';

const history = createMemoryHistory();
const storeState = {
[lookupCodesSlice.name]: { lookupCodes: mockLookups },
};

const mockGetApi = {
error: undefined,
response: [1] as number[] | undefined,
execute: jest.fn().mockResolvedValue([1]),
loading: false,
};
jest.mock('@/components/common/mapFSM/MapStateMachineContext');
jest.mock('@/hooks/repositories/usePimsPropertyRepository', () => ({
usePimsPropertyRepository: () => {
return {
getMatchingProperties: mockGetApi,
};
},
}));

describe('FilterContentContainer component', () => {
let viewProps: IFilterContentFormProps;

const View = forwardRef<FormikProps<any>, IFilterContentFormProps>((props, ref) => {
viewProps = props;
return <></>;
});

const setup = (
renderOptions?: RenderOptions & { props?: Partial<IFilterContentContainerProps> },
) => {
renderOptions = renderOptions ?? {};
const utils = render(<FilterContentContainer {...renderOptions.props} View={View} />, {
...renderOptions,
store: storeState,
history,
});

return {
...utils,
};
};

beforeEach(() => {
jest.resetAllMocks();
(useMapStateMachine as jest.Mock).mockImplementation(() => mapMachineBaseMock);
});

afterEach(() => {
jest.clearAllMocks();
});

it('fetches filter data from the api', async () => {
mockGetApi.execute.mockResolvedValue(getMockApiPropertyManagement(1));
setup({});
viewProps.onChange(new PropertyFilterFormModel());
expect(mockGetApi.execute).toBeCalledWith(new PropertyFilterFormModel().toApi());
await waitFor(() =>
expect(mapMachineBaseMock.setVisiblePimsProperties).toBeCalledWith({
additionalDetails: 'test',
id: 1,
isLeaseActive: false,
isLeaseExpired: false,
isTaxesPayable: null,
isUtilitiesPayable: null,
leaseExpiryDate: null,
managementPurposes: [],
rowVersion: 1,
}),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Api_PropertyFilterCriteria } from '@/models/api/ProjectFilterCriteria';
import { IFilterContentFormProps } from './FilterContentForm';
import { PropertyFilterFormModel } from './models';

interface IFilterContentContainerProps {
export interface IFilterContentContainerProps {
View: React.FunctionComponent<IFilterContentFormProps>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createMemoryHistory } from 'history';

import { mockLookups } from '@/mocks/lookups.mock';
import { getMockApiPropertyManagement } from '@/mocks/propertyManagement.mock';
import { Api_PropertyManagement } from '@/models/api/Property';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { act, render, RenderOptions, userEvent } from '@/utils/test-utils';

import { FilterContentForm, IFilterContentFormProps } from './FilterContentForm';

const history = createMemoryHistory();
const storeState = {
[lookupCodesSlice.name]: { lookupCodes: mockLookups },
};

const mockGetApi = {
error: undefined,
response: undefined as Api_PropertyManagement | undefined,
execute: jest.fn(),
loading: false,
};

jest.mock('@/hooks/repositories/usePropertyManagementRepository', () => ({
usePropertyManagementRepository: () => {
return {
getPropertyManagement: mockGetApi,
};
},
}));

describe('FilterContentForm component', () => {
const onChange = jest.fn();

const setup = (renderOptions: RenderOptions & { props: IFilterContentFormProps }) => {
renderOptions = renderOptions ?? ({} as any);
const utils = render(<FilterContentForm {...renderOptions.props} />, {
...renderOptions,
store: storeState,
history,
});

return {
...utils,
};
};

afterEach(() => {
jest.clearAllMocks();
});

it('shows loading spinner when loading', () => {
mockGetApi.execute.mockResolvedValue(getMockApiPropertyManagement(1));
const { getByTestId } = setup({ props: { onChange, isLoading: true } });
expect(getByTestId('filter-backdrop-loading')).toBeVisible();
});

it('displays filters when not loading', async () => {
const apiManagement = getMockApiPropertyManagement(1);
mockGetApi.response = apiManagement;
const { getByDisplayValue } = setup({ props: { onChange, isLoading: false } });
expect(getByDisplayValue('Select a highway')).toBeVisible();
expect(getByDisplayValue('Select Lease Transaction')).toBeVisible();
});

it('calls onChange when a filter is changed', async () => {
const apiManagement = getMockApiPropertyManagement(1);
mockGetApi.response = apiManagement;
const { getByTestId } = setup({ props: { onChange, isLoading: false } });
await act(async () => {
userEvent.selectOptions(getByTestId('leasePayRcvblType'), ['all']);
expect(onChange).toBeCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ export const FilterContentForm: React.FC<React.PropsWithChildren<IFilterContentF
return { codeType: x.id.toString(), codeTypeDescription: x.name };
});

const leasePaymentRcvblOptions = getByType(API.LEASE_PAYMENT_RECEIVABLE_TYPES).map<SelectOption>(
x => {
return { value: x.id.toString(), label: x.name };
},
);
leasePaymentRcvblOptions.push({ value: 'all', label: 'Payable and Receivable' });

return (
<Formik<PropertyFilterFormModel> initialValues={initialFilter} onSubmit={noop}>
<Form>
Expand Down Expand Up @@ -122,6 +129,18 @@ export const FilterContentForm: React.FC<React.PropsWithChildren<IFilterContentF
</SectionField>
</Section>
<Section header="Lease / License" isCollapsable initiallyExpanded>
<SectionField
label="Lease Transaction"
contentWidth="12"
tooltip="Selecting the Payable and Receivable lease transaction option will display properties that have both a payable and a receivable lease on them."
>
<Select
field="leasePayRcvblType"
placeholder="Select Lease Transaction"
options={leasePaymentRcvblOptions}
data-testid="leasePayRcvblType"
/>
</SectionField>
<SectionField label="Status" contentWidth="12">
<Select
field="leaseStatus"
Expand Down
Loading

0 comments on commit 93697c0

Please sign in to comment.