diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index fb036dc7..3ff2b7ef 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -170,6 +170,10 @@ const endpoints = Object.freeze({ // eslint-disable-next-line max-len url: (accountNumber, policyNumber, phoneNumber) => `/order/api/v1/order/duplicate-check/${accountNumber}/${policyNumber}/${phoneNumber}`, method: 'GET' + }, + FinalDeductible: { + url: '/coverage/api/v1/coverage/final-deductible', + method: 'POST' } }); diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index 68ce7fd0..7b701ea2 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -797,8 +797,8 @@ describe.skip('coverageStatement.vue', () => { }); describe('coverageStatement.vue-working', () => { - describe('Navigation', () => { - test('Function called on any FORWARD navigation', () => { + describe('ITAC flag', () => { + test('ITAC flag updated once component is initialized', () => { // Arrange const mainInitialState = { order: { @@ -810,7 +810,7 @@ describe('coverageStatement.vue-working', () => { const { wrapper } = getMountedComponent(mainInitialState); // Act - wrapper.vm.forwardButtonAction(); + wrapper.vm.initializeComponent(); // Assert expect(wrapper.vm.mainStore.updatePolicyITACFlag) diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 4554fedc..3db996f3 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -147,6 +147,7 @@ export default { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to?.query?.issPage); const supportingItemsPromise = await useMainStore().getSupportingItems(); + const finalDeductiblePromise = await useMainStore().getFinalDeductible(); // Settle promises and get results const promiseResultMap = [ @@ -157,6 +158,10 @@ export default { { resultKey: 'supportingItems', promise: supportingItemsPromise + }, + { + resultKey: 'finalDeductible', + promise: finalDeductiblePromise } ]; @@ -247,13 +252,8 @@ export default { return damageString === 'match' ? '' : damageString; }, vehicleDeductible() { - if (useMainStore().order.damage.isRepair) { - const repairDeductible = useMainStore().order.policy.deductible.repair; - return repairDeductible; - } - - const replaceDeductible = useMainStore().order.policy.deductible.replace; - return replaceDeductible; + const deductible = useMainStore().order.currentDeductible; + return deductible; }, formattedDeductible() { return this.getDeductibleString(this.vehicleDeductible); @@ -341,6 +341,7 @@ export default { return !!useMainStore().vehicle.carId; }, async initializeComponent() { + useMainStore().updatePolicyITACFlag(this.verifiedITAC); if (this.policyLookupSuccessful && useMainStore().isClaimRegistrationRequired && (this.coveredAndServicePriceAboveOrEqualDeductible || this.verifiedITAC)) { @@ -352,7 +353,6 @@ export default { return this.navigateForward(); }, async navigateForward() { - useMainStore().updatePolicyITACFlag(this.verifiedITAC); if (this.unverified || this.verifiedDeductible) { this.mainStore.saveSupportingItems(this.supportingItems); this.$router.navigate( diff --git a/src/layouts/policy-endorsements/policy-endorsements.vue b/src/layouts/policy-endorsements/policy-endorsements.vue index 362a7589..e5d637fb 100644 --- a/src/layouts/policy-endorsements/policy-endorsements.vue +++ b/src/layouts/policy-endorsements/policy-endorsements.vue @@ -67,6 +67,8 @@ import settleAllPromises from '@/helpers/layout-helper'; import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import globalRules from '@/constants/global-rules.js'; +import endorsementOptions from '@/constants/endorsement-options.js'; + import { useMainStore } from '@/store'; export default { @@ -126,10 +128,10 @@ export default { return this.getCmsContent('ParkingLotQuestion', 'Answers'); }, educatorEndorsement() { - return useMainStore().order.policy.endorsements?.includes('Educator') ?? false; + return useMainStore().order.policy.endorsements?.includes(endorsementOptions.EDUCATOR) ?? false; }, parkingGuardEndorsement() { - return useMainStore().order.policy.endorsements?.includes('Parking Guard') ?? false; + return useMainStore().order.policy.endorsements?.includes(endorsementOptions.PARKING_GUARD) ?? false; } }, methods: @@ -140,23 +142,21 @@ export default { async forwardButtonAction() { // TO DO: remove hard coding and update data format once service returns endorsement questions if (this.educatorEndorsement) { - this.questionAnswersArray.push( - { + this.questionAnswersArray.push({ questionNum: 1, - endorsement: 'Educator', + endorsementName: endorsementOptions.EDUCATOR, questionText: this.schoolPropertyQuestionText, selectedAnswer: this.schoolPropertyAnswer }); - }; + } if (this.parkingGuardEndorsement) { - this.questionAnswersArray.push( - { + this.questionAnswersArray.push({ questionNum: 2, - endorsement: 'Parking Guard', + endorsementName: endorsementOptions.PARKING_GUARD, questionText: this.parkingLotQuestionText, selectedAnswer: this.parkingLotAnswer }); - }; + } // save answers to store as order.policy.endorsementQuestionAnswers useMainStore().saveEndorsementQuestionAnswers(this.questionAnswersArray); diff --git a/src/store/index.js b/src/store/index.js index 3e5e8cd3..eb69a5df 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -14,6 +14,7 @@ import coverageStatuses from '@/constants/coverage-statuses'; import { deepEqual } from '@/helpers/object-helper'; import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants'; import getDateDifferenceInDays from '@/helpers/date-helper'; +import endorsementOptions from '@/constants/endorsement-options'; const storeId = 'main'; @@ -79,7 +80,8 @@ const getDefaultState = () => ({ isITAC: false, vehicles: [], endorsements: [], - endorsementQuestionAnswers: null, + endorsementQuestionAnswers: [], + status: null, policyData: null }, customer: { @@ -513,6 +515,45 @@ export const useMainStore = defineStore({ }); }); }, + getFinalDeductible() { + const endorsementAnswersForPayload = []; + const endorsementAnswers = this.order.policy.endorsementQuestionAnswers; + if (endorsementAnswers) { + endorsementAnswers.forEach((endorsement) => { + if (endorsement.selectedAnswer === 'Yes') { + endorsementAnswersForPayload.push(endorsement.endorsementName); + } + }); + } + + const manualGlassNamesArray = []; + const glassInformation = this.order.damage.glassToReplace; + glassInformation?.forEach((glassPiece) => { + manualGlassNamesArray.push(glassPiece?.glassLocation?.toUpperCase()); + }); + + return new Promise((resolve, reject) => { + globalMethods.callHttpClient({ + method: endpoints.FinalDeductible.method, + endpoint: endpoints.FinalDeductible.url, + payload: { + referralCorrelationId: this.order.referralCorrelationId, + accountNumber: this.issConfig.parentAccountNumber?.toString() ?? '', + endorsements: endorsementAnswersForPayload, + manualGlassNames: manualGlassNamesArray, + policyState: this.order.customer.address.state, + status: this.order.policy.status, + currentDeductible: this.order.currentDeductible, + noCoverage: this.order.policy.noCoverage, + isRepair: this.order.damage.isRepair + } + }).then((r) => { + this.updateDeductible(r.data); + return resolve(r); + }).catch((error) => reject(error)); + }); + }, + getDuplicateReferrals() { return new Promise((resolve, reject) => { globalMethods.callHttpClient({ @@ -1560,6 +1601,11 @@ export const useMainStore = defineStore({ updateIsSafeliteProvider(isSafelite) { this.order.serviceLocation.IsSafeliteProvider = isSafelite; }, + + updateDeductible(finalDeductible) { + this.order.currentDeductible = finalDeductible; + }, + savePartQuestionAnswers(partQuestionAnswersArray) { // if part question answers have changed, reset subsequent question answers const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.partQuestionAnswers, 'result'); diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 2e122e20..eb59c670 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1694,4 +1694,190 @@ describe('Store', () => { expect(store.isDropOffAppointment).toBe(false); }); }); + + describe('getFinalDeductible method', () => { + describe('successful method call', () => { + it('calls get final deductible api endpoint', () => { + // Arrange + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + store.order.damage.glassToReplace = [getRandomString(10, 15)]; + + // Act + store.getFinalDeductible(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({ + method: endpoints.FinalDeductible.method, + endpoint: endpoints.FinalDeductible.url + })); + }); + it('returns expected response object', async () => { + // Arrange + const response = { deductible: getRandomInt(0, 5000) }; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); + store.order.damage.glassToReplace = [getRandomString(10, 15)]; + + // Act + const result = store.getFinalDeductible(); + + // Assert + await expect(result).resolves.toBe(response); + }); + it('calls api with expected payload', () => { + // Arrange + const location = getRandomString(10, 15).toUpperCase(); + store.order.damage.glassToReplace = [ + { + glassLocation: location + } + ]; + const referralCorrelationId = getRandomGuid(); + const accountNumber = getRandomString(6, 6); + const endorsements = []; + const manualGlassNames = [location]; + const policyState = getRandomString(2, 2); + const status = getRandomString(6, 8); + const currentDeductible = getRandomInt(0, 5000); + const noCoverage = getRandomBoolean(); + const isRepair = getRandomBoolean(); + store.order.referralCorrelationId = referralCorrelationId; + store.issConfig.parentAccountNumber = accountNumber; + store.order.currentDeductible = currentDeductible; + store.order.policy.noCoverage = noCoverage; + store.order.policy.status = status; + store.order.customer.address.state = policyState; + store.order.damage.isRepair = isRepair; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.getFinalDeductible(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(({ + method: endpoints.FinalDeductible.method, + endpoint: endpoints.FinalDeductible.url, + payload: ({ + referralCorrelationId, + accountNumber, + endorsements, + manualGlassNames, + policyState, + status, + currentDeductible, + noCoverage, + isRepair + }) + })); + }); + it('only "Yes" endorsement answers are added to payload', () => { + // Arrange + const location = getRandomString(10, 15).toUpperCase(); + store.order.damage.glassToReplace = [ + { + glassLocation: location + } + ]; + const referralCorrelationId = getRandomGuid(); + const accountNumber = getRandomString(6, 6); + const manualGlassNames = [location]; + const policyState = getRandomString(2, 2); + const status = getRandomString(6, 8); + const currentDeductible = getRandomInt(0, 5000); + const noCoverage = getRandomBoolean(); + const isRepair = getRandomBoolean(); + const endorsementAnswers = [ + { + questionNum: getRandomInt(1, 10), + endorsementName: getRandomString(8, 20), + questionText: getRandomString(50, 100), + selectedAnswer: 'Yes' + }, + { + questionNum: getRandomInt(1, 10), + endorsementName: getRandomString(8, 20), + questionText: getRandomString(50, 100), + selectedAnswer: 'Yes' + }, + { + questionNum: getRandomInt(1, 10), + endorsementName: getRandomString(8, 20), + questionText: getRandomString(50, 100), + selectedAnswer: 'No' + } + ]; + store.order.referralCorrelationId = referralCorrelationId; + store.issConfig.parentAccountNumber = accountNumber; + store.order.currentDeductible = currentDeductible; + store.order.policy.noCoverage = noCoverage; + store.order.policy.status = status; + store.order.customer.address.state = policyState; + store.order.policy.endorsementQuestionAnswers = endorsementAnswers; + store.order.damage.isRepair = isRepair; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.getFinalDeductible(); + + // Assert + const expected = [endorsementAnswers[0].endorsementName, endorsementAnswers[1].endorsementName]; + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(({ + method: endpoints.FinalDeductible.method, + endpoint: endpoints.FinalDeductible.url, + payload: ({ + referralCorrelationId, + accountNumber, + endorsements: expected, + manualGlassNames, + policyState, + status, + currentDeductible, + noCoverage, + isRepair + }) + })); + }); + }); + describe('unsuccessful api call', () => { + it('api call throws exception', async () => { + // Arrange + expect.assertions(2); + const error = 'final deductible error'; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); + + // Act + await store.getFinalDeductible().catch((e) => { + expect(e).toEqual(error); + }); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({ + method: endpoints.FinalDeductible.method, + endpoint: endpoints.FinalDeductible.url + })); + }); + }); + }); + + describe('updateDeductible method', () => { + it('final deductible is saved as currentDeductible in store', () => { + // Arrange + const finalDeductible = getRandomInt(0, 5000); + + // Act + store.updateDeductible(finalDeductible); + + // Assert + expect(store.order.currentDeductible).toEqual(finalDeductible); + }); + it('null final deductible => currentDeductible in store set to null', () => { + // Arrange + const finalDeductible = null; + + // Act + store.updateDeductible(finalDeductible); + + // Assert + expect(store.order.currentDeductible).toEqual(finalDeductible); + }); + }); });