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

Exempt state for MHR Information #1591

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 8 additions & 5 deletions ppr-ui/src/components/unitNotes/UnitNoteContentInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- Note Information-->
<div>
<v-row
v-if="note.effectiveDateTime && hasEffectiveDateInPanel(note)"
v-if="note.effectiveDateTime && hasEffectiveDateInPanel(note) && !isExemptionNoteType"
no-gutters
class="mt-7"
data-test-id="effective-date-info"
Expand Down Expand Up @@ -42,7 +42,7 @@
</v-col>
</v-row>

<v-row no-gutters class="mt-6 mb-7">
<v-row no-gutters class="mt-6" :class="{ 'mb-6': !isExemptionNoteType }" data-test-id="remarks-info">
<v-col cols="3">
<h3 class="fs-14">Remarks</h3>
</v-col>
Expand All @@ -61,12 +61,12 @@
</v-row>

<v-divider
v-if="note.effectiveDateTime || note.expiryDateTime || note.remarks"
v-if="!isExemptionNoteType && (note.effectiveDateTime || note.expiryDateTime || note.remarks)"
class="ml-0 my-4"
/>

<!-- Person Giving Notice or Collector Table -->
<v-row no-gutters class="mt-7">
<v-row v-if="!isExemptionNoteType" no-gutters class="mt-7" data-test-id="person-giving-notice-info">
<v-col cols="3">
<h3 class="fs-14">{{ contactInfoTitle }}</h3>
</v-col>
Expand Down Expand Up @@ -174,7 +174,10 @@ export default defineComponent({
props.note.documentType === UnitNoteDocTypes.NOTICE_OF_TAX_SALE
? collectorInformationContent.title
: personGivingNoticeContent.title
)
),
isExemptionNoteType: computed((): boolean =>
[UnitNoteDocTypes.RESIDENTIAL_EXEMPTION_ORDER, UnitNoteDocTypes.NON_RESIDENTIAL_EXEMPTION]
.includes(props.note.documentType))
})

const getNoticePartyIcon = (givingNoticeParty: PartyIF): string => {
Expand Down
2 changes: 1 addition & 1 deletion ppr-ui/src/components/unitNotes/UnitNotePanel.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-expansion-panel class="unit-note-panel pb-4 px-1">
<v-expansion-panel class="unit-note-panel pb-3 px-1">
<v-expansion-panel-header disable-icon-rotate :disabled="disabled">
<UnitNoteHeaderInfo :note="note"/>
<!-- Custom Panel Actions -->
Expand Down
24 changes: 19 additions & 5 deletions ppr-ui/src/components/unitNotes/UnitNotePanels.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
</template>

<!-- Drop down list -->
<v-list width="350" height="305">
<v-list width="350" class="add-unit-note-item-list">
<v-list-item
v-for="item in UnitNotesDropdown"
v-for="item in addUnitNoteDropdown"
:key="item"
class="unit-note-list-item"
@click="initUnitNote(item)"
Expand Down Expand Up @@ -77,10 +77,14 @@
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs, watch } from 'vue-demi'
import { computed, defineComponent, reactive, toRefs, watch } from 'vue-demi'
import { RouteNames, UnitNoteDocTypes } from '@/enums'
import { useStore } from '@/store/store'
import { UnitNotesInfo, UnitNotesDropdown } from '@/resources'
import { UnitNotesInfo,
UnitNotesDropdown,
ResidentialExemptionStaffDropDown,
ResidentialExemptionQSDropDown }
from '@/resources'
import { UnitNoteIF } from '@/interfaces/unit-note-interfaces'
import UnitNotePanel from './UnitNotePanel.vue'
import { useMhrUnitNotePanel, useNavigation } from '@/composables'
Expand All @@ -98,12 +102,17 @@ export default defineComponent({
disabled: {
type: Boolean,
default: false
},
hasActiveExemption: {
type: Boolean,
default: false
}
},
setup (props) {
const { goToRoute } = useNavigation()

const {
isRoleStaffReg,
setMhrUnitNoteType
} = useStore()

Expand All @@ -113,7 +122,11 @@ export default defineComponent({

const localState = reactive({
activePanels: [],
panelUnitNotes: createPanelUnitNotes(props.unitNotes)
panelUnitNotes: createPanelUnitNotes(props.unitNotes),
addUnitNoteDropdown: computed((): UnitNoteDocTypes[] => {
const dropdown = isRoleStaffReg ? ResidentialExemptionStaffDropDown : ResidentialExemptionQSDropDown
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@cameron-eyds @doug-lovett for awareness, I'm using this reg staff role (ppr_staff), but across the app we also do use 'staff'.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks Dima, the API looks at ppr_staff, not staff, when checking endpoint access.

return props.hasActiveExemption ? dropdown : UnitNotesDropdown
})
})

const initUnitNote = (noteType: UnitNoteDocTypes): void => {
Expand All @@ -129,6 +142,7 @@ export default defineComponent({
initUnitNote,
UnitNotesInfo,
UnitNotesDropdown,
ResidentialExemptionStaffDropDown,
...toRefs(localState)
}
}
Expand Down
25 changes: 19 additions & 6 deletions ppr-ui/src/composables/exemption/useExemptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
hasTruthyValue,
parseAccountToSubmittingParty
} from '@/utils'
import { ExemptionIF, MhRegistrationSummaryIF } from '@/interfaces'
import { APIMhrDescriptionTypes, MhApiStatusTypes, RouteNames, UnitNoteDocTypes } from '@/enums'
import { ExemptionIF, MhRegistrationSummaryIF, UnitNoteIF } from '@/interfaces'
import { APIMhrDescriptionTypes, MhApiStatusTypes, RouteNames, UnitNoteDocTypes, UnitNoteStatusTypes } from '@/enums'

export const useExemptions = () => {
const { goToRoute } = useNavigation()
Expand All @@ -20,7 +20,8 @@ export const useExemptions = () => {
getMhrExemption,
getMhrExemptionValidation,
isRoleStaffReg,
isRoleQualifiedSupplier
isRoleQualifiedSupplier,
getMhrUnitNotes
} = storeToRefs(useStore())

/** Returns true when staff or qualified supplier and the feature flag is enabled **/
Expand Down Expand Up @@ -104,20 +105,32 @@ export const useExemptions = () => {
}
}

/** Check is MHR Registration has filed Residential Exemption **/
/** Check if MHR Registration has filed Residential Exemption **/
const hasChildResExemption = (mhrRegSummary: MhRegistrationSummaryIF): boolean => {
return mhrRegSummary.changes?.filter(
reg =>
reg.registrationDescription === APIMhrDescriptionTypes.RESIDENTIAL_EXEMPTION &&
[APIMhrDescriptionTypes.RESIDENTIAL_EXEMPTION.toString(),
APIMhrDescriptionTypes.NON_RESIDENTIAL_EXEMPTION.toString()].includes(reg.registrationDescription) &&
(reg.statusType === MhApiStatusTypes.EXEMPT || reg.statusType === MhApiStatusTypes.ACTIVE)
).length > 0
}

/* Get active Residential Exemption from unit notes */
const getActiveExemption = () => {
// there should be only one active residential exemption
return getMhrUnitNotes.value.find((unitNote: UnitNoteIF) =>
[UnitNoteDocTypes.RESIDENTIAL_EXEMPTION_ORDER, UnitNoteDocTypes.NON_RESIDENTIAL_EXEMPTION]
.includes(unitNote.documentType) &&
unitNote.status === UnitNoteStatusTypes.ACTIVE
)
}

return {
isExemptionEnabled,
goToExemptions,
updateValidation,
buildExemptionPayload,
hasChildResExemption
hasChildResExemption,
getActiveExemption
}
}
3 changes: 2 additions & 1 deletion ppr-ui/src/enums/registrationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ export enum APIMhrDescriptionTypes {
TRANSFER_EXECUTOR_PROBATE_WILL = 'TRANSFER TO EXECUTOR \u2013 GRANT OF PROBATE WITH WILL',
TRANSFER_EXECUTOR_UNDER_25_WILL = 'TRANSFER TO EXECUTOR \u2013 ESTATE UNDER $25,000 WITH WILL',
TRANSFER_ADMINISTRATOR = 'TRANSFER TO ADMINISTRATOR \u2013 GRANT OF ADMINISTRATION',
RESIDENTIAL_EXEMPTION = 'RESIDENTIAL EXEMPTION'
RESIDENTIAL_EXEMPTION = 'RESIDENTIAL EXEMPTION',
NON_RESIDENTIAL_EXEMPTION = 'NON-RESIDENTIAL EXEMPTION'
}

export enum UIMhrDescriptionTypes {
Expand Down
2 changes: 1 addition & 1 deletion ppr-ui/src/resources/exemptions/componentContent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ContactInformationContentIF, ContentIF, RequirementsConfigIF } from '@/interfaces'
import { ContentIF, RequirementsConfigIF } from '@/interfaces'

export const exDocIdContent: ContentIF = {
title: 'Document ID',
Expand Down
11 changes: 11 additions & 0 deletions ppr-ui/src/resources/unitNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ export const NoticeOfTaxSaleDropDown: Array<UnitNoteDocTypes> = [
UnitNoteDocTypes.NOTICE_OF_REDEMPTION
]

// Dropdown items for Staff when Mhr has Residential Exemption note
export const ResidentialExemptionStaffDropDown: Array<UnitNoteDocTypes> = [
UnitNoteDocTypes.PUBLIC_NOTE,
UnitNoteDocTypes.CONFIDENTIAL_NOTE
]

// Dropdown items for QS when Mhr has Residential Exemption note
export const ResidentialExemptionQSDropDown: Array<UnitNoteDocTypes> = [
UnitNoteDocTypes.PUBLIC_NOTE
]

export const UnitNotesInfo: Record<UnitNoteDocTypes, UnitNoteInfoIF> = {
[UnitNoteDocTypes.NOTICE_OF_CAUTION]: {
header: 'Notice of Caution',
Expand Down
21 changes: 17 additions & 4 deletions ppr-ui/src/views/mhrInformation/MhrInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
This is the current information for this registration as of
<span class="font-weight-bold">{{ asOfDateTime }}</span>.
</p>
<p>Ensure ALL of the information below is correct before making any changes to this
registration. Necessary fees will be applied as updates are made.
<p v-if="!hasActiveExemption" data-test-id="correct-into-desc">
Ensure ALL of the information below is correct before making any changes to this registration.
Necessary fees will be applied as updates are made.
</p>

<!-- Unit Note Info -->
Expand All @@ -51,11 +52,12 @@
</span>

<!-- Has Alert Message (Notice of Tax Sale, and others) -->
<template v-if="hasAlertMsg">
<template v-if="hasAlertMsg || hasActiveExemption">
<CautionBox
class="mt-9"
:setMsg="alertMsg"
setAlert
data-test-id="mhr-alert-msg"
>
<template #prependSLot>
<v-icon color="error" class="alert-icon mt-1 mr-2">mdi-alert</v-icon>
Expand Down Expand Up @@ -239,7 +241,7 @@
<img class="home-owners-icon mb-1 ml-1" src="@/assets/svgs/homeownersicon_reviewscreen.svg"/>
<span class="font-weight-bold pl-2">Home Owners</span>
</v-col>
<v-col v-if="enableHomeOwnerChanges" cols="3" class="text-right">
<v-col v-if="enableHomeOwnerChanges && !hasActiveExemption" cols="3" class="text-right">
<v-btn
text
id="home-owners-change-btn"
Expand Down Expand Up @@ -310,6 +312,7 @@
id="unit-note-component"
:unitNotes="getMhrUnitNotes"
:disabled="!enableHomeOwnerChanges || showTransferType"
:hasActiveExemption="hasActiveExemption"
/>

<v-spacer class="py-10 my-10"></v-spacer>
Expand Down Expand Up @@ -363,6 +366,7 @@ import {
} from '@/components/common'
import {
useAuth,
useExemptions,
useHomeOwners,
useInputRules,
useMhrInformation,
Expand Down Expand Up @@ -510,6 +514,8 @@ export default defineComponent({
isTransferToExecutorUnder25Will
} = useTransferOwners()

const { getActiveExemption } = useExemptions()

// Refs
const homeOwnersComponentRef = ref(null) as Component
const transferDetailsComponent = ref(null) as Component
Expand All @@ -535,6 +541,7 @@ export default defineComponent({
showCancelDialog: false,
showCancelChangeDialog: false,
showStartTransferRequiredDialog: false,
hasActiveExemption: computed((): boolean => !!getActiveExemption()),
transferRequiredDialogOptions: computed((): DialogOptionsIF => {
transferRequiredDialog.text =
transferRequiredDialog.text.replace('mhr_number', getMhrInformation.value.mhrNumber)
Expand Down Expand Up @@ -585,6 +592,12 @@ export default defineComponent({
}),
hasAlertMsg: false,
alertMsg: computed((): string => {
// msg when MHR has a Residential Exemption
if (localState.hasActiveExemption) {
return isRoleStaffReg.value
? `This manufactured home is exempt as of ${pacificDate(getActiveExemption().createDateTime)} and changes can no longer be made to this home unless it is restored. See Unit Notes for further information.` // eslint-disable-line max-len
: `This manufactured home has been exempt as of ${pacificDate(getActiveExemption().createDateTime)} and changes can no longer be made to this home unless it is restored. If you require further information please contact BC Registries staff. ` // eslint-disable-line max-len
}
// not all MHR Info will have the frozenDocumentType
if (!getMhrInformation.value?.frozenDocumentType && !localState.hasAlertMsg) return
// display alert message based o the locker document type
Expand Down
28 changes: 27 additions & 1 deletion ppr-ui/tests/unit/MhrInformation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import {
mockedUnitNotes5,
mockedPerson2,
mockedExecutor,
mockedAdministrator
mockedAdministrator,
mockedResidentialExemptionOrder,
mockedUnitNotes3
} from './test-data'
import {
CertifyIF,
Expand Down Expand Up @@ -1040,4 +1042,28 @@ describe('Mhr Information', () => {
expect(CautionBoxComponent.find('.v-icon').classes('alert-icon')).toBeTruthy()
expect(CautionBoxComponent.text()).toContain(UnitNotesInfo[UnitNoteDocTypes.NOTICE_OF_TAX_SALE].header)
})

it('should have read only view for exempt MHR (Residential Exemption filed)', async () => {
wrapper = createComponent()

// add unit notes with Residential Exemption
await store.setMhrUnitNotes([mockedResidentialExemptionOrder, ...mockedUnitNotes3])
await store.setAuthRoles([AuthRoles.PPR_STAFF])
wrapper.vm.dataLoaded = true
await nextTick()

expect(wrapper.find(getTestId('correct-into-desc')).exists()).toBeFalsy()
expect(wrapper.find(getTestId('mhr-alert-msg')).exists()).toBeTruthy()
// message for Staff should contain unique text
expect(wrapper.find(getTestId('mhr-alert-msg')).text()).toContain('See Unit Notes for further information')
expect(wrapper.find(HomeOwners).find('#home-owners-change-btn').exists()).toBeFalsy()

// setup Qualified Supplier as Manufacturer
await store.setAuthRoles([AuthRoles.MHR_TRANSFER_SALE])
await store.setUserProductSubscriptionsCodes([ProductCode.MANUFACTURER])
await nextTick()

// message for QS should contain unique text
expect(wrapper.find(getTestId('mhr-alert-msg')).text()).toContain('contact BC Registries staff')
})
})
12 changes: 6 additions & 6 deletions ppr-ui/tests/unit/Tombstone.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AccountInformationIF, FinancingStatementIF, UserInfoIF } from '@/interf
import { mockedFinancingStatementComplete, mockedSelectSecurityAgreement } from './test-data'
import mockRouter from './MockRouter'
import { AuthRoles, ProductCode, RouteNames } from '@/enums'
import { convertDate, pacificDate } from '@/utils'
import { pacificDate } from '@/utils'

Vue.use(Vuetify)

Expand Down Expand Up @@ -150,7 +150,7 @@ describe('Tombstone component', () => {
expect(extraInfo.length).toBe(0)
})

it('renders Tombstone component peoperly for Search', async () => {
it('renders Tombstone component properly for Search', async () => {
wrapper = createComponent(RouteNames.SEARCH)
await nextTick()

Expand All @@ -168,7 +168,7 @@ describe('Tombstone component', () => {
expect(extraInfo.length).toBe(0)
})

it('renders Tombstone component peoperly for New Registration: length-trust', async () => {
it('renders Tombstone component properly for New Registration: length-trust', async () => {
wrapper = createComponent(RouteNames.LENGTH_TRUST)
await nextTick()

Expand All @@ -186,7 +186,7 @@ describe('Tombstone component', () => {
expect(extraInfo.length).toBe(0)
})

it('renders Tombstone component peoperly for New Registration: parties/debtors', async () => {
it('renders Tombstone component properly for New Registration: parties/debtors', async () => {
wrapper = createComponent(RouteNames.ADD_SECUREDPARTIES_AND_DEBTORS)
await nextTick()

Expand All @@ -204,7 +204,7 @@ describe('Tombstone component', () => {
expect(extraInfo.length).toBe(0)
})

it('renders Tombstone component peoperly for New Registration: collateral', async () => {
it('renders Tombstone component properly for New Registration: collateral', async () => {
wrapper = createComponent(RouteNames.ADD_COLLATERAL)
await nextTick()

Expand All @@ -222,7 +222,7 @@ describe('Tombstone component', () => {
expect(extraInfo.length).toBe(0)
})

it('renders Tombstone component peoperly for New Registration: review/confirm', async () => {
it('renders Tombstone component properly for New Registration: review/confirm', async () => {
wrapper = createComponent(RouteNames.REVIEW_CONFIRM)
await nextTick()

Expand Down
Loading
Loading