From 9ff890727584f0f89a93be90a9e8cf11558b8541 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 17 Nov 2022 16:19:13 -0500 Subject: [PATCH 1/2] CSR-504 | Quote page service calls Call GetWipers, GetRainDefense, GetSupportingItems, GetPricing GetPricing is mocked for now GetSupportingItems will change and need to be modified Fixed a few bugs related to loading session/order Modified how we save parts to lineITems --- src/constants/endpoints.js | 16 ++++++ src/constants/store-actions.js | 4 ++ src/layouts/quote/quote.vue | 83 ++++++++------------------- src/mixins/vehicle-questions-mixin.js | 2 + src/store/index.js | 55 +++++++++++++++++- 5 files changed, 99 insertions(+), 61 deletions(-) diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index aeb38d792..3024a67d9 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -62,6 +62,18 @@ const endpoints = { url: "/parts/api/v1/parts/parts", method: "POST", }, + GetWipers: { + url: "/parts/api/v1/parts/wipers", + method: "GET", + }, + GetRainDefense: { + url: "/parts/api/v1/parts/rain-defense", + method: "GET", + }, + GetSupportingItems: { + url: "/parts/api/v1/parts/supporting-items", + method: "POST", + }, GetCapabilityQuestions: { url: "/parts/api/v1/parts/capability-questions", method: "GET", @@ -82,6 +94,10 @@ const endpoints = { url: "/location/api/v1/location/zip", method: "GET", }, + PriceOrderItems: { + url: "/price/api/v1/price/order-items", + method: "POST", + }, LogExperimentExposureIfAssigned: { url: "/experiments/api/v1/experiments/log-exposure", method: "POST", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 78053b567..d3890886b 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -20,6 +20,9 @@ const storeActions = { LOOKUP_VIN_BY_ADDRESS: "lookupVinByAddress", GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", GET_PARTS: "getParts", + GET_WIPERS: "getWipers", + GET_RAIN_DEFENSE: 'getRainDefense', + GET_SUPPORTING_ITEMS: 'getSupportingItems', GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions", GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer", GET_MOLDING_QUESTIONS: "getMoldingQuestions", @@ -27,6 +30,7 @@ const storeActions = { LOAD_SESSION: "loadSession", UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse", VALIDATE_ZIP: "validateZip", + PRICE_ORDER_ITEMS: "priceOrderItems", LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure", LOG_PAGE_VIEW: "logPageView", LOG_CUSTOM_EVENT: "logCustomEvent", diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 43f24ae82..9269faea6 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -57,7 +57,9 @@ import textBlock from "@/common-components/text-block/text-block"; import modal from "@/common-components/modal/modal"; import baseMixin from "@/mixins/base-mixin.js"; import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin"; - +import { settleAllPromises } from "@/helpers/layout-helper"; +import { storeActions } from "@/constants/store-actions"; +import store from "@/store"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { Form } from "vee-validate"; @@ -66,70 +68,35 @@ export default { async beforeRouteEnter(to, from, next) { // Call APIs const cmsContent = await fetchCmsContentForPage(to.query.fmgPage); + const wipersPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_WIPERS); + const rainDefensePromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_RAIN_DEFENSE); + const supportingItemsPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_SUPPORTING_ITEMS); - // TODOS - modify as needed, just a rough sketch to place initializations + const promiseResultMap = [ + { + resultKey: "wipers", + promise: wipersPromise, + }, + { + resultKey: "rainDefense", + promise: rainDefensePromise, + }, + { + resultKey: "supportingItems", + promise: supportingItemsPromise, + }, + ]; - // get supporting availableLineItems + const resultMap = await settleAllPromises(promiseResultMap); + const glassParts = [...store.getters.order.lineItems.glassParts]; + const availableLineItems = [resultMap.rainDefense, ...resultMap.supportingItems, ...resultMap.wipers, ...glassParts]; - // get wiper and rain defense availableLineItems - - // Settle promises and get results - - // get price of supporting, rain defense & wipers, and parts - - // Settle pricing promise and get results + const pricingResults = await baseMixin.methods.dispatchStoreAction(storeActions.PRICE_ORDER_ITEMS, availableLineItems, false); // Call the "next" function to complete the transition to this page. next((vm) => { vm.setCmsContent(cmsContent); - vm.availableLineItems = [ - { - partNumber: "001", - Description: "Windshield with Recal", - partType: "Windshield", - Quantity: "1", - BasePartNumber: "001", - Color: "Green", - CanSafeliteRecalibrate: true, - price: 350.22, - }, - { - partNumber: "SBB16", - description: "SAFELITE BEAM BLADE 16", - partType: "FRONT WIPER", - price: 32.64, - }, - { - partNumber: "A DISPOSAL FEE", - partType: "DISPOSAL FEE", - price: 15.0, - }, - { - partNumber: "SBB26", - description: "SAFELITE BEAM BLADE 26", - partType: "FRONT WIPER", - price: 53.04, - }, - { - partNumber: "SBBR12A", - description: "SAFELITE REAR BLADE 12A", - partType: "REAR WIPER", - price: 24.48, - }, - { - partNumber: "RAIN DEFENSE", - description: null, - partType: "RAIN DEFENSE", - price: 35.5, - }, - { - partNumber: "RECAL STATIC", - Description: "Recalibration", - partType: "recalibration", - Quantity: "1", - price: 150.0, - }, - ]; + vm.availableLineItems = pricingResults; vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); }); }, diff --git a/src/mixins/vehicle-questions-mixin.js b/src/mixins/vehicle-questions-mixin.js index 0a3ce43f9..d7fac4330 100644 --- a/src/mixins/vehicle-questions-mixin.js +++ b/src/mixins/vehicle-questions-mixin.js @@ -42,6 +42,8 @@ export default { partNumber: singlePart.partNumber, description: singlePart.description, color: singlePart.color, + partType: singlePart.partType, + canSafeliteRecalibrate: singlePart.canSafeliteRecalibrate, requiresRecalibration: singlePart.requiresRecalibration, requiresCapabilityQuestions: singlePart.requiresCapabilityQuestions, recalibrationType: singlePart.recalibrationType, diff --git a/src/store/index.js b/src/store/index.js index 309304714..ce585510e 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -342,7 +342,7 @@ export const mutations = { state.order.damage.isRepair = sessionInformation.order.damage.isRepair; state.order.damage.numberOfChips = sessionInformation.order.damage.numberOfChips; - state.order.lineItems.glassParts = sessionInformation.order.lineItems.parts; + state.order.lineItems.glassParts = sessionInformation.order.lineItems.glassParts; state.order.accountNumber = sessionInformation.order.accountNumber; state.order.providerNumber = sessionInformation.order.providerNumber; (state.order.serviceLocation.address = @@ -871,6 +871,47 @@ export const actions = { return response; }, + getWipers(context) { + const carId = context.getters.vehicle.carId; + const serviceZipCode = context.getters.order.serviceLocation.zipCode; + return globalMethods.callHttpClient({ + method: endpoints.GetWipers.method, + endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`, + }).catch(error => { + // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow + return []; + }); + }, + + getRainDefense(context) { + return globalMethods.callHttpClient({ + method: endpoints.GetRainDefense.method, + endpoint: endpoints.GetRainDefense.url, + }); + }, + + getSupportingItems(context) { + const glassPartsArray = context.getters.lineItems.glassParts; + if (!glassPartsArray) { + return []; + } + const carId = context.getters.vehicle.carId; + const isRepair = context.getters.damage.isRepair; + const numberOfChips = context.getters.damage.numberOfChips; + const accountNumber = "82791";//context.getters.order.accountNumber.toString(); + return globalMethods.callHttpClient({ + method: endpoints.GetSupportingItems.method, + endpoint: endpoints.GetSupportingItems.url, + payload: { + carId: carId, + serviceType: isRepair ? "Repair" : "Replace", + //accountNumber: accountNumber, + parts: glassPartsArray, + numberOfRepairChips: isRepair ? numberOfChips : 0 + }, + }); + }, + getCapabilityQuestions(context, { carId, partNumber }) { return globalMethods.callHttpClient({ method: endpoints.GetCapabilityQuestions.method, @@ -984,7 +1025,7 @@ export const actions = { }) .then((response) => { // Flatten location and name properties - response.data.damage?.glassToReplace?.map((glass) => { + response.data.order.damage?.glassToReplace?.map((glass) => { glass.glassLocation = glass.location; glass.glassName = glass.name; delete glass.location; @@ -1311,6 +1352,14 @@ export const actions = { capabilityQuestionAnswers ); }, + // Price order actions + // PLACEHOLDER, WILL CHANGE WHEN PRICING END POINT IS IMPLEMENTED + priceOrderItems(context, availableLineItems) { + availableLineItems.forEach((lineItem => { + lineItem["price"] = parseFloat((Math.random() * 100).toFixed(2)); + })); + return availableLineItems; + }, // Misc order actions saveServiceLocation(context, serviceLocationInfo) { context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); @@ -1394,7 +1443,7 @@ function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { } function convertGlassPieceNamingForApi(glassArray) { - if (!glassArray) return []; + if (!glassArray || glassArray.length === 0) return []; // check if array already converted. (likely when a session has been saved previously and then reloaded) if (glassArray[0].location !== undefined) { From 181de2d386532306f006666f4d70fbfb2241fb84 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 17 Nov 2022 16:37:35 -0500 Subject: [PATCH 2/2] CSR-504 | Fix unit tests and formatting --- src/constants/store-actions.js | 4 ++-- src/layouts/quote/quote.vue | 21 +++++++++++++++++---- src/store/index.js | 26 ++++++++++++++------------ src/store/store.spec.js | 9 +++++---- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index d3890886b..11e2ae4c5 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -21,8 +21,8 @@ const storeActions = { GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", GET_PARTS: "getParts", GET_WIPERS: "getWipers", - GET_RAIN_DEFENSE: 'getRainDefense', - GET_SUPPORTING_ITEMS: 'getSupportingItems', + GET_RAIN_DEFENSE: "getRainDefense", + GET_SUPPORTING_ITEMS: "getSupportingItems", GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions", GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer", GET_MOLDING_QUESTIONS: "getMoldingQuestions", diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 263b86b10..5c903ea7e 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -69,8 +69,12 @@ export default { // Call APIs const cmsContent = await fetchCmsContentForPage(to.query.fmgPage); const wipersPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_WIPERS); - const rainDefensePromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_RAIN_DEFENSE); - const supportingItemsPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_SUPPORTING_ITEMS); + const rainDefensePromise = baseMixin.methods.dispatchStoreAction( + storeActions.GET_RAIN_DEFENSE + ); + const supportingItemsPromise = baseMixin.methods.dispatchStoreAction( + storeActions.GET_SUPPORTING_ITEMS + ); const promiseResultMap = [ { @@ -89,9 +93,18 @@ export default { const resultMap = await settleAllPromises(promiseResultMap); const glassParts = [...store.getters.order.lineItems.glassParts]; - const availableLineItems = [resultMap.rainDefense, ...resultMap.supportingItems, ...resultMap.wipers, ...glassParts]; + const availableLineItems = [ + resultMap.rainDefense, + ...resultMap.supportingItems, + ...resultMap.wipers, + ...glassParts, + ]; - const pricingResults = await baseMixin.methods.dispatchStoreAction(storeActions.PRICE_ORDER_ITEMS, availableLineItems, false); + const pricingResults = await baseMixin.methods.dispatchStoreAction( + storeActions.PRICE_ORDER_ITEMS, + availableLineItems, + false + ); // Call the "next" function to complete the transition to this page. next((vm) => { diff --git a/src/store/index.js b/src/store/index.js index ce585510e..253b404de 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -874,13 +874,15 @@ export const actions = { getWipers(context) { const carId = context.getters.vehicle.carId; const serviceZipCode = context.getters.order.serviceLocation.zipCode; - return globalMethods.callHttpClient({ - method: endpoints.GetWipers.method, - endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`, - }).catch(error => { - // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow - return []; - }); + return globalMethods + .callHttpClient({ + method: endpoints.GetWipers.method, + endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`, + }) + .catch((error) => { + // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow + return []; + }); }, getRainDefense(context) { @@ -898,7 +900,7 @@ export const actions = { const carId = context.getters.vehicle.carId; const isRepair = context.getters.damage.isRepair; const numberOfChips = context.getters.damage.numberOfChips; - const accountNumber = "82791";//context.getters.order.accountNumber.toString(); + const accountNumber = "82791"; //context.getters.order.accountNumber.toString(); return globalMethods.callHttpClient({ method: endpoints.GetSupportingItems.method, endpoint: endpoints.GetSupportingItems.url, @@ -907,8 +909,8 @@ export const actions = { serviceType: isRepair ? "Repair" : "Replace", //accountNumber: accountNumber, parts: glassPartsArray, - numberOfRepairChips: isRepair ? numberOfChips : 0 - }, + numberOfRepairChips: isRepair ? numberOfChips : 0, + }, }); }, @@ -1355,9 +1357,9 @@ export const actions = { // Price order actions // PLACEHOLDER, WILL CHANGE WHEN PRICING END POINT IS IMPLEMENTED priceOrderItems(context, availableLineItems) { - availableLineItems.forEach((lineItem => { + availableLineItems.forEach((lineItem) => { lineItem["price"] = parseFloat((Math.random() * 100).toFixed(2)); - })); + }); return availableLineItems; }, // Misc order actions diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 063dedd76..d99e42b82 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -662,7 +662,7 @@ describe("Actions", () => { const context = state; globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { referralNumber: 123 } }); + return Promise.resolve({ data: { referralNumber: 123, order: {} } }); }); const commit = jest.fn(); @@ -675,9 +675,10 @@ describe("Actions", () => { }); // Assert - expect(response.data).toEqual({ referralNumber: 123 }); + expect(response.data).toEqual({ referralNumber: 123, order: {} }); expect(commit).toBeCalledWith(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, { referralNumber: 123, + order: {}, }); }); @@ -686,7 +687,7 @@ describe("Actions", () => { const context = state; globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { eon: "123" } }); + return Promise.resolve({ data: { eon: "123", order: {} } }); }); context.commit = jest.fn(); @@ -709,7 +710,7 @@ describe("Actions", () => { const context = state; globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { eon: "123" } }); + return Promise.resolve({ data: { eon: "123", order: {} } }); }); context.commit = jest.fn();