diff --git a/public/static/healthcheck/index.html b/public/static/healthcheck/index.html index 52e21bb5b..3f87a7587 100644 --- a/public/static/healthcheck/index.html +++ b/public/static/healthcheck/index.html @@ -545,7 +545,6 @@ { path: '/parts/parts-or-questions', method: 'POST', body: {"carId":"CR00046193","glassPieces":[{"location":"Windshield","name":"Single"}],"zip":"43015","vin":null,"serviceType":null,"referralSeqNumber":environmentContext.referralNumber,"AppName":"FixMyGlass"} }, { path: '/parts/wipers/CR00046193/43015', method: 'GET' }, { path: '/parts/mobile-premium-fee/Cash/Replace', method: 'GET' }, - { path: '/parts/service-package-discount', method: 'POST', body: {"glassPieces":[{"location":"Windshield","name":"Single"}],"damageType":"Replace","AppName":"FixMyGlass"} }, { path: '/parts/mobile-fee/?serviceType=Install&facilityType=Mobile&parentAccountNumber=167132&billToAccountNumber=87291&providerNumber=03357&isItacOptimized=false&zipCode=43015', method: 'GET' }, ] }, diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index c1771b3f5..ba11ad3a1 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -76,10 +76,6 @@ const endpoints = { url: "/parts/api/v1/parts/pricing-by-day-part-number", method: "GET", }, - GetServicePackageDiscountPart: { - url: "/parts/api/v1/parts/service-package-discount", - method: "POST", - }, GetServiceabilityDetails: { url: "/location/api/v1/location/serviceability-details", method: "GET", @@ -216,6 +212,10 @@ const endpoints = { url: "/order/api/v1/order/add-donation-part", method: "POST", }, + GetOemAfterMarketParts: { + url: "/parts/api/v1/parts/oem-after-market-parts", + method: "POST", + }, }; export { endpoints }; diff --git a/src/constants/experiments.js b/src/constants/experiments.js index f358fc89f..bec7c9a39 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -33,6 +33,7 @@ const experimentSettings = { SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays", SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays", SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays", + OFFER_OEM: "OfferOem", }; const experimentTriggers = { diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 513891ea1..e09a4c78b 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -23,6 +23,7 @@ const storeActions = { GET_ALERT_REASONS_BY_CTU: "getAlertReasonsByCtu", GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", GET_PARTS: "getParts", + GET_OEM_AFTERMARKET_PARTS: "getOemAfterMarketParts", GET_WIPERS: "getWipers", GET_RAIN_REPEL: "getRainRepel", GET_SUPPORTING_ITEMS: "getSupportingItems", @@ -32,7 +33,6 @@ const storeActions = { GET_MOLDING_QUESTIONS: "getMoldingQuestions", GET_MOBILE_FEE_PART: "getMobileFeePart", GET_PRICING_BY_DAY_PART: "getPricingByDayPart", - GET_SERVICE_PACKAGE_DISCOUNT_PART: "getServicePackageDiscountPart", GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails", GET_SHOP_TIME_SLOTS: "getShopTimeSlots", GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots", diff --git a/src/helpers/service-package-helper.js b/src/helpers/service-package-helper.js index 4144c44e7..d3fc3f01b 100644 --- a/src/helpers/service-package-helper.js +++ b/src/helpers/service-package-helper.js @@ -254,6 +254,19 @@ export function getDiscountedPackageName(discountPackage) { return package_names[discountPackage] || null; } +export function getPackageLabel(packageName) { + switch (packageName) { + case packageNames.TIER_ONE: + return externalParamPackageLabels.GLASS_ONLY; + case packageNames.TIER_TWO: + return externalParamPackageLabels.STANDARD; + case packageNames.TIER_THREE: + return externalParamPackageLabels.PREMIUM; + default: + return null; + } +} + function maxTier(tierA, tierB) { if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) { return packageNames.TIER_THREE; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 01b585349..b08e9ed72 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -33,6 +33,7 @@ @vapsItemsSelected="vapsItemsSelectedAction" @quotePageDiscountSelected="quotePageDiscountSelectedAction" @currentNumberOfPackages="currentNumberOfPackagesAction" + @servicePackageSelected="servicePackageSelectedAction" :servicePackage="servicePackage" :activePromos="lineItems.promos" :availableQuotePageDiscounts="quoteDiscountPartsInfo" @@ -218,6 +219,19 @@ export default { "quote" ); + // call API oem-after-market-parts method + const offerOem = experimentMixin.methods.hasSettingEqualTo( + experimentSettings.OFFER_OEM, + "true" + ); + if (offerOem) { + const oemAfterMarketParts = baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.GET_OEM_AFTERMARKET_PARTS, + null, + "quote" + ); + } + const shouldExposeSkipQuote = store.getters.isRecalibrationOnOrder; const skipQuoteExperiment = store.getters.applicationUser.experiments.find( (e) => e.universeName === experimentUniverses.IGQ_SkipQuote @@ -564,6 +578,7 @@ export default { isSaveProgressComplete: null, packageNumber: null, skipToInsurance: null, + servicePackageSelected: null, }; }, async mounted() { @@ -670,6 +685,9 @@ export default { vapsItemsSelectedAction(vapsItemsSelected) { this.lineItems.vaps = vapsItemsSelected; }, + servicePackageSelectedAction(newValue) { + this.servicePackageSelected = newValue; + }, quotePageDiscountSelectedAction(supportingItemsSelected) { this.lineItems.supportingItems = supportingItemsSelected; }, @@ -758,7 +776,14 @@ export default { false ); - await savePageData(this.pageName, { saveProgressPopupSkipped: true }, true); + await savePageData( + this.pageName, + { + saveProgressPopupSkipped: true, + servicePackageSelected: this.servicePackageSelected, + }, + true + ); const payment = this.$store.getters.payment; if (payment.isInsurance) { diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index e45147295..436e98b24 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -34,6 +34,7 @@ import { findLineItemsWithPartType, getPackageNameByType, getDiscountedPackageName, + getPackageLabel, } from "@/helpers/service-package-helper"; import { getPromosThatMatchLineItemsOnOrder, @@ -45,6 +46,7 @@ import { containsRecalParts, getItemsWithoutRecalParts } from "@/helpers/recal-h import experimentMixin from "@/mixins/experiment-mixin.js"; import { experimentSettings } from "@/constants/experiments"; import { savePageData } from "@/router/methods/helpers/save-page-data"; +import { debugLog } from "@/helpers/debug-log-helper"; export default { name: "servicePackageQuestion", @@ -84,6 +86,12 @@ export default { if (this.activePromos?.length) this.selectDefaultPackage(); }, selectedPackageName(newValue) { + debugLog("selectedPackageName:", newValue); + const packageLabel = getPackageLabel(newValue); + + debugLog("getPackageLabel:", packageLabel); + this.$emit("servicePackageSelected", `ServicePackage-${packageLabel}`); + const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue); this.$emit("vapsItemsSelected", VapsProductsInSelectedPackage); const discountPartInSelectedPackage = this.getDiscountPartForSelectedPackage(newValue); diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index a99c0b36a..b6809795a 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -1653,6 +1653,8 @@ export default { this.updateSelectedProvider(); }, updateSelectedProvider(newShopProvider) { + if (!this.appointmentTypeStrings) return; + if (newShopProvider) { this.selectedProvider = newShopProvider; } else if (this.appointmentType === this.appointmentTypeStrings.MOBILE) { @@ -1729,7 +1731,9 @@ export default { this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF; } // make sure a selectedProvider exists - this.updateSelectedProvider(this.lastSelectedInshopOrDropoffProvider); + if (this.lastSelectedInshopOrDropoffProvider) { + this.updateSelectedProvider(this.lastSelectedInshopOrDropoffProvider); + } } else { this.appointmentType = null; } @@ -1796,23 +1800,34 @@ export default { } if (this.isShowMobileFirstAppt) { - if (firstMobilePMDate && numberOfDaysToFirstMobilePMDate <= pmMobileDays) { + if (pmMobileDays == 0) { + // Selects the first available time slot + preSelectedMobileAppointment = + numberOfDaysToFirstMobileAMDate <= numberOfDaysToFirstMobilePMDate + ? firstMobileAMAppt + : firstMobilePMAppt; + } else if ( + firstMobilePMDate && + numberOfDaysToFirstMobilePMDate <= pmMobileDays + ) { + // Selects the first available PM time slot preSelectedMobileAppointment = firstMobilePMAppt; } else if ( firstMobilePMDate && numberOfDaysToFirstMobilePMDate >= noPMMobileDays ) { + // Selects the first available AM time slot. If none, selects the first available PM time slot preSelectedMobileAppointment = firstMobileAMAppt ? firstMobileAMAppt - : null; - !preSelectedMobileAppointment && - (preSelectedMobileAppointment = firstMobilePMAppt); + : firstMobilePMAppt; } - this.$refs.mobileFirstModal.setSelectedAppointment( - preSelectedMobileAppointment - ); - this.$refs.mobileFirstModal.openModal(); + if (preSelectedMobileAppointment) { + this.$refs.mobileFirstModal.setSelectedAppointment( + preSelectedMobileAppointment + ); + this.$refs.mobileFirstModal.openModal(); + } } } } diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index fc4d08b92..924b2e62a 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -80,6 +80,7 @@ export default { }, set: function (newValue) { this.$emit("update:modelValue", newValue); + this.$emit("handle-appointment-type-change", newValue); }, }, isMobileOnly() { diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index a77fe930a..0be50a8be 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -232,6 +232,21 @@ export default { (part) => `${part.glassLocation}-${part.glassName}` ); + const format = (part) => `${part?.partNumber}-${part?.partType ? part.partType : ""}`; + const mapParts = (arr) => (arr ?? []).map(format); + + const glassParts = mapParts(order?.lineItems?.glassParts); + const supportingItems = mapParts(order?.lineItems?.supportingItems); + const vaps = mapParts(order?.lineItems?.vaps); + const childParts = [].concat( + ...(order?.lineItems?.glassParts ?? []).map((gp) => mapParts(gp?.childParts)) + ); + const allParts = [...glassParts, ...supportingItems, ...vaps, ...childParts]; + + if (applicationUser?.pageData?.quote?.servicePackageSelected) { + allParts.push(applicationUser.pageData.quote.servicePackageSelected); + } + var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`; var sessionData = {}; @@ -251,7 +266,7 @@ export default { ? "Insurance" : "Cash"; sessionData.damageType = order?.damage?.isRepair ? "Repair" : "Replace"; - sessionData.productType = glassProducts; + sessionData.productType = allParts; sessionData.eon = order?.eon; sessionData.referralNumber = order?.referralNumber; sessionData.referralSequenceNumber = order?.referralSequenceNumber; diff --git a/src/store/index.js b/src/store/index.js index ca1d76f6f..895763ff3 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1836,6 +1836,56 @@ export const actions = { return response; }, + // Parts API Actions + async getOemAfterMarketParts(context, { pageNameToLog }) { + const vehicle = context.getters.vehicle; + const damage = context.getters.damage; + const order = context.state.order; + + const carId = vehicle.carId; + const glassArray = damage.glassToReplace; + const resultsArray = damage.partQuestionAnswers; + const zipCode = order.serviceLocation.zipCode; + const vin = vehicle.vin; + const make = vehicle.make; + const serviceType = order.serviceLocation?.appointmentType; + const referralSeqNumber = order.referralSequenceNumber; + const parentAccountNumber = applicationConfig.CASH_PARENT_ACCOUNT_NUMBER; + const oemEndorsementFlag = false; // Need to update from the store when available + + // create a new array to avoid mutating state + const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); + const resultsArrayForPayload = convertResultsForApi(resultsArray); + + const response = await globalMethods.callHttpClient({ + method: endpoints.GetOemAfterMarketParts.method, + endpoint: endpoints.GetOemAfterMarketParts.url, + payload: { + carId: carId, + glassPieces: glassArrayForPayload, + answerResults: resultsArrayForPayload, + zip: zipCode, + vin: vin, + serviceType: serviceType, + referralSeqNumber: referralSeqNumber, + make: make, + parentAccountNumber: parentAccountNumber.toString(), + oemEndorsementFlag: oemEndorsementFlag, + }, + logApiCall: true, + pageNameToLog: pageNameToLog, + }); + + // Flatten location and name properties + if (response?.data?.glassPieceParts) { + response.data.glassPieceParts = convertGlassPieceNamingFromApi( + response.data.glassPieceParts + ); + } + + return response; + }, + async getWipers(context, { payload, pageNameToLog }) { const carId = payload.carId; const serviceZipCode = payload.serviceZipCode; @@ -1936,26 +1986,6 @@ export const actions = { return response; }); }, - getServicePackageDiscountPart(context, { pageNameToLog }) { - const damage = context.getters.damage; - const damageType = damage.isRepair ? "Repair" : "Replace"; - const glassPieces = convertGlassPieceNamingForApi(damage.glassToReplace); - - return globalMethods - .callHttpClient({ - method: endpoints.GetServicePackageDiscountPart.method, - endpoint: endpoints.GetServicePackageDiscountPart.url, - payload: { - glassPieces: glassPieces, - damageType: damageType, - }, - logApiCall: true, - pageNameToLog: pageNameToLog, - }) - .then((response) => { - return response; - }); - }, getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) { const escapeRecalibrationType = (rt) => rt.split("&").join("%26");