diff --git a/source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs b/source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs index 72f733c04e..72c8220eb9 100644 --- a/source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs +++ b/source/backend/apimodels/Models/Concepts/Lease/LeaseMap.cs @@ -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() @@ -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); } diff --git a/source/backend/apimodels/Models/Concepts/Lease/LeaseModel.cs b/source/backend/apimodels/Models/Concepts/Lease/LeaseModel.cs index 4aaa60387a..06bd7e40a4 100644 --- a/source/backend/apimodels/Models/Concepts/Lease/LeaseModel.cs +++ b/source/backend/apimodels/Models/Concepts/Lease/LeaseModel.cs @@ -211,6 +211,21 @@ public class LeaseModel : FileWithChecklistModel /// get/set - The project associated with this lease. /// public ProjectModel Project { get; set; } + + /// + /// get/set - is public benefit. + /// + public bool? IsPublicBenefit { get; set; } + + /// + /// get/set is financial gain. + /// + public bool? IsFinancialGain { get; set; } + + /// + /// get/set - fee determination notes. + /// + public string FeeDeterminationNote { get; set; } #endregion } } diff --git a/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts b/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts index 62e047dadc..af01226202 100644 --- a/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts +++ b/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts @@ -114,6 +114,9 @@ export const AddLeaseYupSchema = Yup.object().shape({ primaryArbitrationCity: Yup.string() .nullable() .max(200, 'Primary arbitration city must be at most ${max} characters'), + 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'), diff --git a/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx new file mode 100644 index 0000000000..a1ba4f94dc --- /dev/null +++ b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.test.tsx @@ -0,0 +1,129 @@ +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 = {}, + ) => { + // render component under test + const component = await renderAsync( + + {formikProps => } + , + { + ...renderOptions, + claims: [], + store: storeState, + history, + }, + ); + + return { + component, + }; + }; + it('renders as expected', async () => { + const { component } = await setup({}); + expect(component.asFragment()).toMatchSnapshot(); + }); + + it('displays expected Nominal fee', async () => { + const { + component: { container }, + } = 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', 'false', 'select'); + }); + + expect(suggestedFeeField).toHaveTextContent('$1 - Nominal'); + }); + + it('displays expected LAF fee', async () => { + const { + component: { container }, + } = 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) *'); + }); + + it('displays expected FMV fee', async () => { + const { + component: { container }, + } = await setup({}); + + let suggestedFeeField = await container.querySelector("span[data-testid='suggestedFee']"); + + expect(suggestedFeeField).toHaveTextContent('Unknown'); + + await act(async () => { + await fillInput(container, 'isPublicBenefit', 'false', 'select'); + await fillInput(container, 'isFinancialGain', 'true', 'select'); + }); + + expect(suggestedFeeField).toHaveTextContent('Fair Market Value (FMV) - (Licence Administration Fee Minimum)'); + }); + + it('displays expected LAF fee', async () => { + const { + component: { container }, + } = 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) *'); + }); + + it('displays expected LAF fee', async () => { + const { + component: { container }, + } = 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) *'); + }); +}); diff --git a/source/frontend/src/features/leases/add/FeeDeterminationSubForm.tsx b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.tsx new file mode 100644 index 0000000000..3e7f989770 --- /dev/null +++ b/source/frontend/src/features/leases/add/FeeDeterminationSubForm.tsx @@ -0,0 +1,60 @@ +import { FormikProps, getIn } from 'formik'; +import { useEffect, useState } from 'react'; + +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; +} + +const FeeDeterminationSubForm: React.FunctionComponent = ({ + 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 ( +
+ + + + + + + + + + {fee} + + + +