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-8317: Lease and Licence Edit/View Fee Denomination forms #4187

Merged
merged 16 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
6 changes: 6 additions & 0 deletions source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public void Register(TypeAdapterConfig config)
.Map(dest => dest.FileChecklistItems, src => src.PimsLeaseChecklistItems)
.Map(dest => dest.PrimaryArbitrationCity, src => src.PrimaryArbitrationCity)
.Map(dest => dest.Periods, src => src.PimsLeasePeriods)
.Map(dest => dest.IsPublicBenefit, src => src.IsPublicBenefit)
.Map(dest => dest.IsFinancialGain, src => src.IsFinancialGain)
.Map(dest => dest.FeeDeterminationNote, src => src.FeeDeterminationNote)
.Map(dest => dest.Renewals, src => src.PimsLeaseRenewals);

config.NewConfig<LeaseModel, PimsLease>()
Expand Down Expand Up @@ -104,6 +107,9 @@ 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.IsPublicBenefit, src => src.IsPublicBenefit)
.Map(dest => dest.IsFinancialGain, src => src.IsFinancialGain)
.Map(dest => dest.FeeDeterminationNote, src => src.FeeDeterminationNote)
.Map(dest => dest.PimsLeaseRenewals, src => src.Renewals)
.IgnoreNullValues(true);
}
Expand Down
15 changes: 15 additions & 0 deletions source/backend/apimodels/Models/Concepts/Lease/LeaseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,21 @@ public class LeaseModel : FileWithChecklistModel
/// get/set - The project associated with this lease.
/// </summary>
public ProjectModel Project { get; set; }

/// <summary>
/// get/set - is public benefit.
/// </summary>
public bool? IsPublicBenefit { get; set; }

/// <summary>
/// get/set is financial gain.
/// </summary>
public bool? IsFinancialGain { get; set; }

/// <summary>
/// get/set - fee determination notes.
/// </summary>
public string FeeDeterminationNote { get; set; }
#endregion
}
}
5 changes: 5 additions & 0 deletions source/frontend/src/features/leases/add/AddLeaseYupSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ export const AddLeaseYupSchema = Yup.object().shape({
primaryArbitrationCity: Yup.string()
.nullable()
.max(200, 'Primary arbitration city must be at most ${max} characters'),
isPublicBenefit: Yup.string().nullable(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

As far as I can tell, these are defined as booleans in the frontend model.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Taken out the nullable validation on the yup schema since is not needed.

isFinancialGain: Yup.string().nullable(),
feeDeterminationNote: Yup.string()
.nullable()
.max(1000, 'Fee determination notes must be at most ${max} characters'),
renewals: Yup.array().of(
Yup.object().shape({
renewalNote: Yup.string().max(2000, 'Renewal note must be at most ${max} characters'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { act, getByText, render, RenderOptions } from '@testing-library/react';
import { getDefaultFormLease, LeaseFormModel } from '../models';
import { AddLeaseYupSchema } from './AddLeaseYupSchema';
import { createMemoryHistory } from 'history';
import FeeDeterminationSubForm, { IFeeDeterminationSubFormProps } from './FeeDeterminationSubForm';
import { Formik, FormikProps } from 'formik';
import React from 'react';
import { noop } from 'lodash';
import { fillInput, renderAsync } from '@/utils/test-utils';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { mockLookups } from '@/mocks/lookups.mock';
import { Simulate } from 'react-dom/test-utils';

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

describe('LeaseFeeDeterminationSubForm component', () => {
const setup = async (
renderOptions: RenderOptions & Partial<IFeeDeterminationSubFormProps> = {},
) => {
// render component under test
const component = await renderAsync(
<Formik onSubmit={noop} initialValues={getDefaultFormLease()}>
{formikProps => <FeeDeterminationSubForm formikProps={formikProps} />}
</Formik>,
{
...renderOptions,
claims: [],
store: storeState,
history,
},
);

return {
component,
};
};
it('renders as expected', async () => {
const { component } = await setup({});
expect(component.asFragment()).toMatchSnapshot();
});

it('displays expected suggested fee', async () => {
const {
component: { container, getByText },
} = await setup({});

let suggestedFeeField = await container.querySelector("span[data-testid='suggestedFee']");

expect(suggestedFeeField).toHaveTextContent('Unknown');

await act(async () => {
await fillInput(container, 'isPublicBenefit', 'true', 'select');
await fillInput(container, 'isFinancialGain', 'true', 'select');
});

expect(suggestedFeeField).toHaveTextContent('Licence Administration Fee (LAF) *');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FormikProps, getIn } from 'formik';
import { useEffect, useState } from 'react';
import styled from 'styled-components';

import { TextArea } from '@/components/common/form';
import { InlineYesNoSelect } from '@/components/common/form/styles';
import { Section } from '@/components/common/Section/Section';
import { SectionField } from '@/components/common/Section/SectionField';

import { getSuggestedFee } from '../leaseUtils';
import { LeaseFormModel } from '../models';

export interface IFeeDeterminationSubFormProps {
formikProps: FormikProps<LeaseFormModel>;
}

const FeeDeterminationSubForm: React.FunctionComponent<IFeeDeterminationSubFormProps> = ({
formikProps,
}) => {
const { values } = formikProps;
const isPublicBenefit = getIn(values, 'isPublicBenefit');
const financialGain = getIn(values, 'isFinancialGain');

const [fee, setFee] = useState('');

useEffect(() => {
setFee(getSuggestedFee(isPublicBenefit, financialGain));
}, [isPublicBenefit, financialGain]);

return (
<Section header="Fee Determination">
<SectionField label="Public benefit" labelWidth="2" contentWidth="8">
<InlineYesNoSelect field="isPublicBenefit" />
</SectionField>

<SectionField label="Financial gain" labelWidth="2" contentWidth="8">
<InlineYesNoSelect field="isFinancialGain" />
</SectionField>

<SectionField
label="Sugested fee"
tooltip="If the financial gain far outweighs the public benefit, Fair Market Value should be considered over Licence Administration Fee."
labelWidth="2"
contentWidth="8"
>
<span data-testid="suggestedFee">{fee}</span>
</SectionField>

<SectionField
label="Notes"
tooltip="Deviations from standard fees should be explained here"
labelWidth="2"
contentWidth="8"
>
<MediumTextArea field="feeDeterminationNote" />
</SectionField>
</Section>
);
};

export default FeeDeterminationSubForm;

const MediumTextArea = styled(TextArea)`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this necessary? I'm surprised that you needed to manually specify the height here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed this since height is not needed

textarea.form-control {
min-width: 100%;
height: 7rem;
resize: none;
}
`;
Loading
Loading